diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py index 559b1c1bd..1e7812903 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py @@ -2,307 +2,383 @@ from eth2spec.test.context import spec_state_test, always_bls, with_electra_and_ from eth2spec.test.helpers.deposits import ( prepare_deposit_request, run_deposit_request_processing, - run_deposit_request_processing_with_specific_fork_version ) -from eth2spec.test.helpers.state import next_epoch_via_block -from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable -from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with -def run_process_pending_deposits(spec, state): - yield from run_epoch_processing_with( - spec, state, 'process_pending_deposits') +def _run_deposit_request_switching_to_compounding( + spec, + state, + validator_index, + initial_creds, + request_creds, + signed=True, + effective=True +): + deposit_request = prepare_deposit_request( + spec, + validator_index, + # Minimal deposit amount + amount=(spec.EFFECTIVE_BALANCE_INCREMENT * 1), + withdrawal_credentials=request_creds, + signed=signed + ) + state.validators[validator_index].withdrawal_credentials = initial_creds + + yield from run_deposit_request_processing( + spec, + state, + deposit_request, + validator_index, + switches_to_compounding=effective + ) + + if effective: + # Withdrawal address must never be changed, the change applies to the type only + expected_credentials = spec.COMPOUNDING_WITHDRAWAL_PREFIX + initial_creds[1:] + assert state.validators[validator_index].withdrawal_credentials == expected_credentials + else: + assert state.validators[validator_index].withdrawal_credentials == initial_creds @with_electra_and_later @spec_state_test -def test_new_deposit_under_max(spec, state): - # fresh deposit = next validator index = validator appended to registry - validator_index = len(state.validators) - # effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement. - amount = spec.MAX_EFFECTIVE_BALANCE - 1 - deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) - - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -def test_new_deposit_max(spec, state): +def test_process_deposit_request_min_activation(spec, state): # fresh deposit = next validator index = validator appended to registry validator_index = len(state.validators) # effective balance will be exactly the same as balance. - amount = spec.MAX_EFFECTIVE_BALANCE + amount = spec.MIN_ACTIVATION_BALANCE deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) @with_electra_and_later @spec_state_test -def test_new_deposit_over_max(spec, state): - # fresh deposit = next validator index = validator appended to registry - validator_index = len(state.validators) - # just 1 over the limit, effective balance should be set MAX_EFFECTIVE_BALANCE during processing - amount = spec.MAX_EFFECTIVE_BALANCE + 1 - deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) - - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -def test_new_deposit_eth1_withdrawal_credentials(spec, state): +def test_process_deposit_request_max_effective_balance_compounding(spec, state): # fresh deposit = next validator index = validator appended to registry validator_index = len(state.validators) + # effective balance will be exactly the same as balance. + amount = spec.MAX_EFFECTIVE_BALANCE_ELECTRA withdrawal_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + deposit_request = prepare_deposit_request( + spec, + validator_index, + amount, + signed=True, + withdrawal_credentials=withdrawal_credentials + ) + + yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_process_deposit_request_top_up_min_activation(spec, state): + validator_index = 0 + amount = spec.MIN_ACTIVATION_BALANCE // 4 + deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) + + state.balances[validator_index] = spec.MIN_ACTIVATION_BALANCE + state.validators[validator_index].effective_balance = spec.MIN_ACTIVATION_BALANCE + + yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_process_deposit_request_top_up_max_effective_balance_compounding(spec, state): + validator_index = 0 + amount = spec.MIN_ACTIVATION_BALANCE // 4 + withdrawal_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + + state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE + state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE + state.validators[validator_index].withdrawal_credentials = withdrawal_credentials + + deposit_request = prepare_deposit_request( + spec, + validator_index, + amount, + signed=True, + withdrawal_credentials=withdrawal_credentials + ) + + yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) + + +@with_electra_and_later +@spec_state_test +@always_bls +def test_process_deposit_request_invalid_sig(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + # effective balance will be exactly the same as balance. + amount = spec.MIN_ACTIVATION_BALANCE + deposit_request = prepare_deposit_request(spec, validator_index, amount) + + yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) + + +@with_electra_and_later +@spec_state_test +@always_bls +def test_process_deposit_request_top_up_invalid_sig(spec, state): + validator_index = 0 + amount = spec.MIN_ACTIVATION_BALANCE // 4 + deposit_request = prepare_deposit_request(spec, validator_index, amount) + + state.balances[validator_index] = spec.MIN_ACTIVATION_BALANCE + state.validators[validator_index].effective_balance = spec.MIN_ACTIVATION_BALANCE + + yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_process_deposit_request_set_start_index(spec, state): + assert state.deposit_requests_start_index == spec.UNSET_DEPOSIT_REQUESTS_START_INDEX + + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + # effective balance will be exactly the same as balance. + amount = spec.MIN_ACTIVATION_BALANCE + deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) + + yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) + + assert state.deposit_requests_start_index == deposit_request.index + + +@with_electra_and_later +@spec_state_test +def test_process_deposit_request_set_start_index_only_once(spec, state): + initial_start_index = 1 + + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + # effective balance will be exactly the same as balance. + amount = spec.MIN_ACTIVATION_BALANCE + deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) + + state.deposit_requests_start_index = initial_start_index + + yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) + + assert state.deposit_requests_start_index == initial_start_index + + +@with_electra_and_later +@spec_state_test +def test_process_deposit_request_switch_to_compounding_normal(spec, state): + validator_index = 0 + initial_withdrawal_credentials = ( spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 # specified 0s + b'\x59' * 20 # a 20-byte eth1 address ) - amount = spec.MAX_EFFECTIVE_BALANCE - deposit_request = prepare_deposit_request( - spec, - validator_index, - amount, - withdrawal_credentials=withdrawal_credentials, - signed=True, + compounding_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address ) - - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -def test_new_deposit_non_versioned_withdrawal_credentials(spec, state): - # fresh deposit = next validator index = validator appended to registry - validator_index = len(state.validators) - withdrawal_credentials = ( - b'\xFF' # Non specified withdrawal credentials version - + b'\x02' * 31 # Garabage bytes - ) - amount = spec.MAX_EFFECTIVE_BALANCE - deposit_request = prepare_deposit_request( - spec, - validator_index, - amount, - withdrawal_credentials=withdrawal_credentials, - signed=True, - ) - - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -@always_bls -def test_correct_sig_but_forked_state(spec, state): - validator_index = len(state.validators) - amount = spec.MAX_EFFECTIVE_BALANCE - # deposits will always be valid, regardless of the current fork - state.fork.current_version = spec.Version('0x1234abcd') - deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - - -@with_electra_and_later -@spec_state_test -@always_bls -def test_incorrect_sig_new_deposit(spec, state): - # fresh deposit = next validator index = validator appended to registry - validator_index = len(state.validators) - amount = spec.MAX_EFFECTIVE_BALANCE - deposit_request = prepare_deposit_request(spec, validator_index, amount) - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index, effective=False) - - -@with_electra_and_later -@spec_state_test -def test_top_up__max_effective_balance(spec, state): - validator_index = 0 - amount = spec.MAX_EFFECTIVE_BALANCE // 4 - deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) - - state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE - state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE - - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - - deposits_len = len(state.pending_deposits) - assert state.pending_deposits[deposits_len - 1].amount == amount - assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE - - -@with_electra_and_later -@spec_state_test -def test_top_up__less_effective_balance(spec, state): - validator_index = 0 - amount = spec.MAX_EFFECTIVE_BALANCE // 4 - deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) - - initial_balance = spec.MAX_EFFECTIVE_BALANCE - 1000 - initial_effective_balance = spec.MAX_EFFECTIVE_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT - state.balances[validator_index] = initial_balance - state.validators[validator_index].effective_balance = initial_effective_balance - - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - - deposits_len = len(state.pending_deposits) - assert state.pending_deposits[deposits_len - 1].amount == amount - # unchanged effective balance - assert state.validators[validator_index].effective_balance == initial_effective_balance - - -@with_electra_and_later -@spec_state_test -def test_top_up__zero_balance(spec, state): - validator_index = 0 - amount = spec.MAX_EFFECTIVE_BALANCE // 4 - deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) - - initial_balance = 0 - initial_effective_balance = 0 - state.balances[validator_index] = initial_balance - state.validators[validator_index].effective_balance = initial_effective_balance - - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - - deposits_len = len(state.pending_deposits) - assert state.pending_deposits[deposits_len - 1].amount == amount - # unchanged effective balance - assert state.validators[validator_index].effective_balance == initial_effective_balance - - -@with_electra_and_later -@spec_state_test -@always_bls -def test_incorrect_sig_top_up(spec, state): - validator_index = 0 - amount = spec.MAX_EFFECTIVE_BALANCE // 4 - deposit_request = prepare_deposit_request(spec, validator_index, amount) - - # invalid signatures, in top-ups, are allowed! - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -def test_incorrect_withdrawal_credentials_top_up(spec, state): - validator_index = 0 - amount = spec.MAX_EFFECTIVE_BALANCE // 4 - withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(b"junk")[1:] - deposit_request = prepare_deposit_request( - spec, - validator_index, - amount, - withdrawal_credentials=withdrawal_credentials - ) - - # inconsistent withdrawal credentials, in top-ups, are allowed! - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -def test_key_validate_invalid_subgroup(spec, state): - validator_index = len(state.validators) - amount = spec.MAX_EFFECTIVE_BALANCE - - # All-zero pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception. - pubkey = b'\x00' * 48 - - deposit_request = prepare_deposit_request(spec, validator_index, amount, pubkey=pubkey, signed=True) - - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -def test_key_validate_invalid_decompression(spec, state): - validator_index = len(state.validators) - amount = spec.MAX_EFFECTIVE_BALANCE - - # `deserialization_fails_infinity_with_true_b_flag` BLS G1 deserialization test case. - # This pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception. - pubkey_hex = 'c01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' - pubkey = bytes.fromhex(pubkey_hex) - - deposit_request = prepare_deposit_request(spec, validator_index, amount, pubkey=pubkey, signed=True) - - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -@always_bls -def test_ineffective_deposit_with_previous_fork_version(spec, state): - # Since deposits are valid across forks, the domain is always set with `GENESIS_FORK_VERSION`. - # It's an ineffective deposit because it fails at BLS sig verification. - # NOTE: it was effective in Altair. - assert state.fork.previous_version != state.fork.current_version - - yield from run_deposit_request_processing_with_specific_fork_version( + + yield from _run_deposit_request_switching_to_compounding( spec, state, - fork_version=state.fork.previous_version, - effective=False, + validator_index, + initial_withdrawal_credentials, + compounding_credentials, + effective=True + ) + + +@with_electra_and_later +@spec_state_test +def test_process_deposit_request_switch_to_compounding_with_excess(spec, state): + validator_index = 0 + # there is excess balace that will be enqueued to pending deposits + initial_balance = initial_effective_balance = ( + spec.MIN_ACTIVATION_BALANCE + spec.EFFECTIVE_BALANCE_INCREMENT // 2) + initial_withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + compounding_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + state.balances[validator_index] = initial_balance + state.validators[validator_index].effective_balance = initial_effective_balance + + yield from _run_deposit_request_switching_to_compounding( + spec, + state, + validator_index, + initial_withdrawal_credentials, + compounding_credentials, + effective=True + ) + + +@with_electra_and_later +@spec_state_test +def test_process_deposit_request_switch_to_compounding_incorrect_credentials(spec, state): + validator_index = 0 + initial_withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + compounding_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + spec.hash(b"junk")[1:] + ) + + yield from _run_deposit_request_switching_to_compounding( + spec, + state, + validator_index, + initial_withdrawal_credentials, + compounding_credentials, + effective=True + ) + + +@with_electra_and_later +@spec_state_test +def test_process_deposit_request_switch_to_compounding_no_compounding(spec, state): + validator_index = 0 + initial_withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + # credentials with ETH1 prefix + incorrect_compounding_credentials = ( + b'\xFF' + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + + yield from _run_deposit_request_switching_to_compounding( + spec, + state, + validator_index, + initial_withdrawal_credentials, + incorrect_compounding_credentials, + effective=False + ) + + +@with_electra_and_later +@spec_state_test +def test_process_deposit_request_switch_to_compounding_has_bls(spec, state): + validator_index = 0 + initial_withdrawal_credentials = state.validators[validator_index].withdrawal_credentials.copy() + compounding_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + + yield from _run_deposit_request_switching_to_compounding( + spec, + state, + validator_index, + initial_withdrawal_credentials, + compounding_credentials, + effective=False ) @with_electra_and_later @spec_state_test @always_bls -def test_effective_deposit_with_genesis_fork_version(spec, state): - assert spec.config.GENESIS_FORK_VERSION not in (state.fork.previous_version, state.fork.current_version) +def test_process_deposit_request_switch_to_compounding_invalid_sig(spec, state): + validator_index = 0 + initial_withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + compounding_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) - yield from run_deposit_request_processing_with_specific_fork_version( + yield from _run_deposit_request_switching_to_compounding( spec, state, - fork_version=spec.config.GENESIS_FORK_VERSION, + validator_index, + initial_withdrawal_credentials, + compounding_credentials, + signed=False, + effective=False ) @with_electra_and_later @spec_state_test -def test_success_top_up_to_withdrawn_validator(spec, state): +def test_process_deposit_request_switch_to_compounding_inactive(spec, state): validator_index = 0 + initial_withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + compounding_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) - # 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 + # Set exit_epoch to the current epoch to make validator inactive + spec.initiate_validator_exit(state, validator_index) + state.validators[validator_index].exit_epoch = spec.get_current_epoch(state) - # Make a top-up balance to validator - amount = spec.MAX_EFFECTIVE_BALANCE // 4 - deposit_request = prepare_deposit_request(spec, validator_index, amount, len(state.validators), signed=True) + yield from _run_deposit_request_switching_to_compounding( + spec, + state, + validator_index, + initial_withdrawal_credentials, + compounding_credentials, + effective=False + ) - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - deposits_len = len(state.pending_deposits) - assert state.pending_deposits[deposits_len - 1].amount == amount - assert state.validators[validator_index].effective_balance == 0 +@with_electra_and_later +@spec_state_test +def test_process_deposit_request_switch_to_compounding_exited_and_active(spec, state): + validator_index = 0 + initial_withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + compounding_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) - validator = state.validators[validator_index] + # Initiate exit + spec.initiate_validator_exit(state, validator_index) - pending_deposits_len = len(state.pending_deposits) - pending_deposit = state.pending_deposits[pending_deposits_len - 1] - current_epoch = spec.get_current_epoch(state) - has_execution_withdrawal = spec.has_execution_withdrawal_credential(validator) - is_withdrawable = validator.withdrawable_epoch <= current_epoch - has_non_zero_balance = pending_deposit.amount > 0 - # NOTE: directly compute `is_fully_withdrawable_validator` conditions here - # to work around how the epoch processing changed balance updates - assert has_execution_withdrawal and is_withdrawable and has_non_zero_balance + yield from _run_deposit_request_switching_to_compounding( + spec, + state, + validator_index, + initial_withdrawal_credentials, + compounding_credentials, + effective=True + ) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index 942fe9ab7..d2f15b0d9 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -1,264 +1,467 @@ -from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with from eth2spec.test.context import ( spec_state_test, with_electra_and_later, + always_bls, +) +from tests.core.pyspec.eth2spec.test.helpers.deposits import ( + prepare_pending_deposit, + run_pending_deposit_applying, ) -from eth2spec.test.helpers.keys import privkeys, pubkeys -from tests.core.pyspec.eth2spec.test.helpers.deposits import build_deposit_data from eth2spec.test.helpers.state import next_epoch_via_block - - -def run_process_pending_deposits(spec, state): - yield from run_epoch_processing_with( - spec, state, 'process_pending_deposits') +from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_add_validator_to_registry(spec, state): +def test_apply_pending_deposit_under_min_activation(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + # effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement. + amount = spec.MIN_ACTIVATION_BALANCE - 1 + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_min_activation(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + # effective balance will be exactly the same as balance. + amount = spec.MIN_ACTIVATION_BALANCE + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_over_min_activation(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + # just 1 over the limit, effective balance should be set MIN_ACTIVATION_BALANCE during processing + amount = spec.MIN_ACTIVATION_BALANCE + 1 + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_eth1_withdrawal_credentials(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + amount = spec.MIN_ACTIVATION_BALANCE + pending_deposit = prepare_pending_deposit( + spec, + validator_index, + amount, + withdrawal_credentials=withdrawal_credentials, + signed=True, + ) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_compounding_withdrawal_credentials_under_max(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + withdrawal_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + # effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement. + amount = spec.MAX_EFFECTIVE_BALANCE_ELECTRA - 1 + pending_deposit = prepare_pending_deposit( + spec, + validator_index, + amount, + withdrawal_credentials=withdrawal_credentials, + signed=True, + ) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_compounding_withdrawal_credentials_max(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + withdrawal_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + # effective balance will be exactly the same as balance. + amount = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + pending_deposit = prepare_pending_deposit( + spec, + validator_index, + amount, + withdrawal_credentials=withdrawal_credentials, + signed=True, + ) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_compounding_withdrawal_credentials_over_max(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + withdrawal_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + # just 1 over the limit, effective balance should be set MAX_EFFECTIVE_BALANCE_ELECTRA during processing + amount = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + 1 + pending_deposit = prepare_pending_deposit( + spec, + validator_index, + amount, + withdrawal_credentials=withdrawal_credentials, + signed=True, + ) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_non_versioned_withdrawal_credentials(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + withdrawal_credentials = ( + b'\xFF' # Non specified withdrawal credentials version + + b'\x02' * 31 # Garabage bytes + ) + amount = spec.MIN_ACTIVATION_BALANCE + pending_deposit = prepare_pending_deposit( + spec, + validator_index, + amount, + withdrawal_credentials=withdrawal_credentials, + signed=True, + ) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_non_versioned_withdrawal_credentials_over_min_activation(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + withdrawal_credentials = ( + b'\xFF' # Non specified withdrawal credentials version + + b'\x02' * 31 # Garabage bytes + ) + # just 1 over the limit, effective balance should be set MIN_ACTIVATION_BALANCE during processing + amount = spec.MIN_ACTIVATION_BALANCE + 1 + pending_deposit = prepare_pending_deposit( + spec, + validator_index, + amount, + withdrawal_credentials=withdrawal_credentials, + signed=True, + ) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +@always_bls +def test_apply_pending_deposit_correct_sig_but_forked_state(spec, state): + validator_index = len(state.validators) + amount = spec.MIN_ACTIVATION_BALANCE + # deposits will always be valid, regardless of the current fork + state.fork.current_version = spec.Version('0x1234abcd') + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +@always_bls +def test_apply_pending_deposit_incorrect_sig_new_deposit(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + amount = spec.MIN_ACTIVATION_BALANCE + pending_deposit = prepare_pending_deposit(spec, validator_index, amount) + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index, effective=False) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_top_up__min_activation_balance(spec, state): + validator_index = 0 + amount = spec.MIN_ACTIVATION_BALANCE // 4 + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) + + state.balances[validator_index] = spec.MIN_ACTIVATION_BALANCE + state.validators[validator_index].effective_balance = spec.MIN_ACTIVATION_BALANCE + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + assert state.balances[validator_index] == spec.MIN_ACTIVATION_BALANCE + amount + assert state.validators[validator_index].effective_balance == spec.MIN_ACTIVATION_BALANCE + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_top_up__min_activation_balance_compounding(spec, state): + validator_index = 0 + withdrawal_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + amount = spec.MIN_ACTIVATION_BALANCE // 4 + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) + + state.balances[validator_index] = spec.MIN_ACTIVATION_BALANCE + state.validators[validator_index].withdrawal_credentials = withdrawal_credentials + state.validators[validator_index].effective_balance = spec.MIN_ACTIVATION_BALANCE + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + assert state.balances[validator_index] == spec.MIN_ACTIVATION_BALANCE + amount + assert state.validators[validator_index].effective_balance == spec.MIN_ACTIVATION_BALANCE + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_top_up__max_effective_balance_compounding(spec, state): + validator_index = 0 + withdrawal_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + amount = spec.MAX_EFFECTIVE_BALANCE_ELECTRA // 4 + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) + + state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + state.validators[validator_index].withdrawal_credentials = withdrawal_credentials + state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + assert state.balances[validator_index] == spec.MAX_EFFECTIVE_BALANCE_ELECTRA + amount + assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE_ELECTRA + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_top_up__less_effective_balance(spec, state): + validator_index = 0 + amount = spec.MIN_ACTIVATION_BALANCE // 4 + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) + + initial_balance = spec.MIN_ACTIVATION_BALANCE - 1000 + initial_effective_balance = spec.MIN_ACTIVATION_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT + state.balances[validator_index] = initial_balance + state.validators[validator_index].effective_balance = initial_effective_balance + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + assert state.balances[validator_index] == initial_balance + amount + # unchanged effective balance + assert state.validators[validator_index].effective_balance == initial_effective_balance + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_top_up__zero_balance(spec, state): + validator_index = 0 + amount = spec.MIN_ACTIVATION_BALANCE // 4 + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) + + initial_balance = 0 + initial_effective_balance = 0 + state.balances[validator_index] = initial_balance + state.validators[validator_index].effective_balance = initial_effective_balance + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + assert state.balances[validator_index] == initial_balance + amount + # unchanged effective balance + assert state.validators[validator_index].effective_balance == initial_effective_balance + + +@with_electra_and_later +@spec_state_test +@always_bls +def test_apply_pending_deposit_incorrect_sig_top_up(spec, state): + validator_index = 0 + amount = spec.MIN_ACTIVATION_BALANCE // 4 + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) + + # invalid signatures, in top-ups, are allowed! + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_incorrect_withdrawal_credentials_top_up(spec, state): + validator_index = 0 + amount = spec.MIN_ACTIVATION_BALANCE // 4 + withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(b"junk")[1:] + pending_deposit = prepare_pending_deposit( + spec, + validator_index, + amount, + signed=True, + withdrawal_credentials=withdrawal_credentials + ) + + # inconsistent withdrawal credentials, in top-ups, are allowed! + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_key_validate_invalid_subgroup(spec, state): + validator_index = len(state.validators) amount = spec.MIN_ACTIVATION_BALANCE - # select validator set outside of the mainnet preset of 256 - index = len(state.validators) - withdrawal_credentials = ( - spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkeys[index])[1:] - ) - deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - withdrawal_credentials, - signed=True) - deposit = spec.PendingDeposit( - pubkey=pubkeys[index], - withdrawal_credentials=withdrawal_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - signature=deposit_data.signature, - ) - old_validator_count = len(state.validators) - state.pending_deposits.append(deposit) - yield from run_process_pending_deposits(spec, state) - # validator count should increase by 1 - assert len(state.validators) == old_validator_count + 1 + # All-zero pubkey would not pass `bls.KeyValidate`, but `apply_pending_deposit` would not throw exception. + pubkey = b'\x00' * 48 + + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, pubkey=pubkey, signed=True) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index, effective=False) @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_increases_balance(spec, state): - amount = 100 - # signature doesn't matter here as it's interpreted as a top-up - deposit = spec.PendingDeposit( - pubkey=state.validators[0].pubkey, - withdrawal_credentials=state.validators[0].withdrawal_credentials, - amount=amount, - slot=spec.GENESIS_SLOT - ) - # run test - spec.apply_pending_deposit(state, deposit) - assert state.balances[0] == amount +def test_apply_pending_deposit_key_validate_invalid_decompression(spec, state): + validator_index = len(state.validators) + amount = spec.MIN_ACTIVATION_BALANCE + + # `deserialization_fails_infinity_with_true_b_flag` BLS G1 deserialization test case. + # This pubkey would not pass `bls.KeyValidate`, but `apply_pending_deposit` would not throw exception. + pubkey_hex = 'c01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + pubkey = bytes.fromhex(pubkey_hex) + + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, pubkey=pubkey, signed=True) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index, effective=False) @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_switch_to_compounding(spec, state): - amount = 100 +@always_bls +def test_apply_pending_deposit_ineffective_deposit_with_bad_fork_version(spec, state): + validator_index=len(state.validators) + fork_version=spec.Version('0xAaBbCcDd') + pending_deposit = prepare_pending_deposit( + spec, + validator_index=validator_index, + amount=spec.MIN_ACTIVATION_BALANCE, + fork_version=fork_version, + signed=True + ) - # choose a value public key that's in the validator set - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index, effective=False) + + +@with_electra_and_later +@spec_state_test +@always_bls +def test_apply_pending_deposit_with_previous_fork_version(spec, state): + # Since deposits are valid across forks, the domain is always set with `GENESIS_FORK_VERSION`. + # It's an ineffective deposit because it fails at BLS sig verification. + # NOTE: it was effective in Altair. + assert state.fork.previous_version != state.fork.current_version + + validator_index=len(state.validators) + fork_version=state.fork.previous_version + pending_deposit = prepare_pending_deposit( + spec, + validator_index=validator_index, + amount=spec.MIN_ACTIVATION_BALANCE, + fork_version=fork_version, + signed=True ) - compounding_credentials = ( - spec.COMPOUNDING_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index, effective=False) + + +@with_electra_and_later +@spec_state_test +@always_bls +def test_ineffective_deposit_with_current_fork_version(spec, state): + validator_index=len(state.validators) + fork_version=state.fork.current_version + pending_deposit = prepare_pending_deposit( + spec, + validator_index=validator_index, + amount=spec.MIN_ACTIVATION_BALANCE, + fork_version=fork_version, + signed=True ) - # advance the state + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index, effective=False) + + +@with_electra_and_later +@spec_state_test +@always_bls +def test_apply_pending_deposit_effective_deposit_with_genesis_fork_version(spec, state): + assert spec.config.GENESIS_FORK_VERSION not in (state.fork.previous_version, state.fork.current_version) + + validator_index=len(state.validators) + fork_version=state.fork.previous_version + pending_deposit = prepare_pending_deposit( + spec, + validator_index=validator_index, + amount=spec.MIN_ACTIVATION_BALANCE, + fork_version=spec.config.GENESIS_FORK_VERSION, + signed=True + ) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_success_top_up_to_withdrawn_validator(spec, state): + validator_index = 0 + + # Fully withdraw validator + set_validator_fully_withdrawable(spec, state, validator_index) + assert state.balances[validator_index] > 0 next_epoch_via_block(spec, state) - state.validators[index].withdrawal_credentials = withdrawal_credentials - # set validator to be exited by current epoch - state.validators[index].exit_epoch = spec.get_current_epoch(state) - 1 - deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - compounding_credentials, - signed=True) - deposit = spec.PendingDeposit( - pubkey=pubkeys[index], - withdrawal_credentials=compounding_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - signature=deposit_data.signature, - ) - state.balances[index] = 0 - # run test - spec.apply_pending_deposit(state, deposit) - # validator balance should increase - assert state.balances[index] == amount - current_credentials = state.validators[0].withdrawal_credentials - assert current_credentials == compounding_credentials - - -@with_electra_and_later -@spec_state_test -def test_apply_pending_deposit_switch_to_compounding_no_compounding(spec, state): - amount = 100 - - # choose a value public key that's in the validator set - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - # wrong compounding - compounding_credentials = ( - spec.hash(b"wrong compounding address")[:] - ) - # advance the state + assert state.balances[validator_index] == 0 + assert state.validators[validator_index].effective_balance > 0 next_epoch_via_block(spec, state) - state.validators[index].withdrawal_credentials = withdrawal_credentials - # set validator to be exited by current epoch - state.validators[index].exit_epoch = spec.get_current_epoch(state) - 1 - deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - compounding_credentials, - signed=True) - deposit = spec.PendingDeposit( - pubkey=pubkeys[index], - withdrawal_credentials=compounding_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - signature=deposit_data.signature, - ) - state.balances[index] = 0 - # run test - spec.apply_pending_deposit(state, deposit) - # validator balance should increase - assert state.balances[index] == amount - current_credentials = state.validators[0].withdrawal_credentials - assert current_credentials == withdrawal_credentials + assert state.validators[validator_index].effective_balance == 0 + # Make a top-up balance to validator + amount = spec.MIN_ACTIVATION_BALANCE // 4 + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) -@with_electra_and_later -@spec_state_test -def test_apply_pending_deposit_switch_to_compounding_has_bls(spec, state): - amount = 100 + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) - # choose a value public key that's in the validator set - index = 0 - compounding_credentials = ( - spec.COMPOUNDING_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - # advance the state - next_epoch_via_block(spec, state) - bls_credentials = state.validators[index].withdrawal_credentials - # set validator to be exited by current epoch - state.validators[index].exit_epoch = spec.get_current_epoch(state) - 1 - deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - compounding_credentials, - signed=True) - deposit = spec.PendingDeposit( - pubkey=pubkeys[index], - withdrawal_credentials=compounding_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - signature=deposit_data.signature, - ) - state.balances[index] = 0 - # run test - spec.apply_pending_deposit(state, deposit) - # validator balance should increase - assert state.balances[index] == amount - current_credentials = state.validators[index].withdrawal_credentials - # does not switch to compounding - assert current_credentials == bls_credentials + assert state.balances[validator_index] == amount + assert state.validators[validator_index].effective_balance == 0 + validator = state.validators[validator_index] + balance = state.balances[validator_index] + current_epoch = spec.get_current_epoch(state) -@with_electra_and_later -@spec_state_test -def test_apply_pending_deposit_switch_to_compounding_invalid_sig(spec, state): - amount = 100 - - # choose a value public key that's in the validator set - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - compounding_credentials = ( - spec.COMPOUNDING_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - # advance the state - next_epoch_via_block(spec, state) - state.validators[index].withdrawal_credentials = withdrawal_credentials - # set validator to be exited by current epoch - state.validators[index].exit_epoch = spec.get_current_epoch(state) - 1 - # creates wrong signature - deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - withdrawal_credentials, - signed=True) - deposit = spec.PendingDeposit( - pubkey=pubkeys[index], - withdrawal_credentials=compounding_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - signature=deposit_data.signature, - ) - state.balances[index] = 0 - # run test - spec.apply_pending_deposit(state, deposit) - # validator balance should increase - assert state.balances[index] == amount - current_credentials = state.validators[0].withdrawal_credentials - assert current_credentials == withdrawal_credentials - - -@with_electra_and_later -@spec_state_test -def test_apply_pending_deposit_switch_to_compounding_not_exited(spec, state): - amount = 100 - - # choose a value public key that's in the validator set - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + spec.hash(pubkeys[index])[1:] - ) - compounding_credentials = ( - spec.COMPOUNDING_WITHDRAWAL_PREFIX + spec.hash(pubkeys[index])[1:] - ) - state.validators[index].withdrawal_credentials = withdrawal_credentials - deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - compounding_credentials, - signed=True) - deposit = spec.PendingDeposit( - pubkey=pubkeys[index], - withdrawal_credentials=compounding_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - signature=deposit_data.signature, - ) - state.balances[index] = 0 - # run test - spec.apply_pending_deposit(state, deposit) - # validator balance should increase - assert state.balances[index] == amount - # make sure validator did not switch to compounding if not exited - current_credentials = state.validators[0].withdrawal_credentials - assert current_credentials == withdrawal_credentials - # postpone pending_deposit - assert len(state.pending_deposits) == 0 + assert spec.is_fully_withdrawable_validator(validator, balance, current_epoch) diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index 72311be22..0137f96be 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -4,6 +4,7 @@ from eth2spec.test.context import expect_assertion_error from eth2spec.test.helpers.forks import is_post_altair, is_post_electra from eth2spec.test.helpers.keys import pubkeys, privkeys from eth2spec.test.helpers.state import get_balance +from eth2spec.test.helpers.epoch_processing import run_epoch_processing_to from eth2spec.utils import bls from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merkle_proof from eth2spec.utils.ssz.ssz_impl import hash_tree_root @@ -23,7 +24,7 @@ def mock_deposit(spec, state, index): assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state)) -def build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, fork_version, signed=False): +def build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, fork_version=None, signed=False): deposit_data = spec.DepositData( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, @@ -276,6 +277,47 @@ def build_pending_deposit(spec, validator_index, amount, pending_deposit.signature = deposit_data.signature return pending_deposit + +def prepare_pending_deposit(spec, validator_index, amount, + pubkey=None, + privkey=None, + withdrawal_credentials=None, + fork_version=None, + signed=False, + slot=None): + """ + Create a pending deposit for the given validator, depositing the given amount. + """ + if pubkey is None: + pubkey = pubkeys[validator_index] + + if privkey is None: + privkey = privkeys[validator_index] + + # insecurely use pubkey as withdrawal key if no credentials provided + if withdrawal_credentials is None: + withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:] + + # use GENESIS_SLOT which is always finalized if no slot provided + if slot is None: + slot = spec.GENESIS_SLOT + + deposit_data = build_deposit_data(spec, + pubkey, + privkey, + amount, + withdrawal_credentials, + fork_version, + signed) + + return spec.PendingDeposit( + pubkey=deposit_data.pubkey, + amount=deposit_data.amount, + withdrawal_credentials=deposit_data.withdrawal_credentials, + signature=deposit_data.signature, + slot=slot, + ) + # # Run processing # @@ -381,14 +423,24 @@ def run_deposit_processing_with_specific_fork_version( yield from run_deposit_processing(spec, state, deposit, validator_index, valid=valid, effective=effective) -def run_deposit_request_processing(spec, state, deposit_request, validator_index, valid=True, effective=True): +def run_deposit_request_processing( + spec, + state, + deposit_request, + validator_index, + effective=True, + switches_to_compounding=False): + """ Run ``process_deposit_request``, yielding: - pre-state ('pre') - deposit_request ('deposit_request') - post-state ('post'). - If ``valid == False``, run expecting ``AssertionError`` """ + assert is_post_electra(spec) + if switches_to_compounding: + assert effective + pre_validator_count = len(state.validators) pre_balance = 0 is_top_up = False @@ -403,71 +455,103 @@ def run_deposit_request_processing(spec, state, deposit_request, validator_index yield 'pre', state yield 'deposit_request', deposit_request - if not valid: - expect_assertion_error(lambda: spec.process_deposit_request(state, deposit_request)) - yield 'post', None - return - spec.process_deposit_request(state, deposit_request) yield 'post', state - if not effective or not bls.KeyValidate(deposit_request.pubkey): + # New validator is only created after the pending_deposits processing + assert len(state.validators) == pre_validator_count + assert len(state.balances) == pre_validator_count + + if is_top_up: + assert state.validators[validator_index].effective_balance == pre_effective_balance + if switches_to_compounding and pre_balance > spec.MIN_ACTIVATION_BALANCE: + assert state.balances[validator_index] == spec.MIN_ACTIVATION_BALANCE + else: + assert state.balances[validator_index] == pre_balance + + pending_deposit = spec.PendingDeposit( + pubkey=deposit_request.pubkey, + withdrawal_credentials=deposit_request.withdrawal_credentials, + amount=deposit_request.amount, + signature=deposit_request.signature, + slot=state.slot, + ) + + if switches_to_compounding and pre_balance > spec.MIN_ACTIVATION_BALANCE: + validator = state.validators[validator_index] + excess_amount = pre_balance - spec.MIN_ACTIVATION_BALANCE + pending_excess = spec.PendingDeposit( + pubkey=validator.pubkey, + withdrawal_credentials=validator.withdrawal_credentials, + amount=excess_amount, + signature=spec.bls.G2_POINT_AT_INFINITY, + slot=spec.GENESIS_SLOT, + ) + assert state.pending_deposits == [pending_deposit, pending_excess] + else: + assert state.pending_deposits == [pending_deposit] + + +def run_pending_deposit_applying(spec, state, pending_deposit, validator_index, effective=True): + """ + Enqueue ``pending_deposit`` and run epoch processing with ``process_pending_deposits``, yielding: + - pre-state ('pre') + - post-state ('post'). + """ + assert is_post_electra(spec) + + # ensure the transition from eth1 bridge is complete + state.deposit_requests_start_index = state.eth1_deposit_index + + # ensure there is enough churn to apply the deposit + if pending_deposit.amount > spec.get_activation_exit_churn_limit(state): + state.deposit_balance_to_consume = pending_deposit.amount - spec.get_activation_exit_churn_limit(state) + + # append pending deposit + state.pending_deposits.append(pending_deposit) + + # run to the very beginning of the epoch processing to avoid + # any updates to the validator registry (e.g. ejections) + run_epoch_processing_to(spec, state, "process_justification_and_finalization") + + pre_validator_count = len(state.validators) + pre_balance = 0 + pre_effective_balance = 0 + is_top_up = False + # is a top-up + if validator_index < pre_validator_count: + is_top_up = True + pre_balance = get_balance(state, validator_index) + pre_effective_balance = state.validators[validator_index].effective_balance + + yield 'pre', state + + spec.process_pending_deposits(state) + + yield 'post', state + + if effective: + if is_top_up: + # Top-ups don't add validators + assert len(state.validators) == pre_validator_count + assert len(state.balances) == pre_validator_count + # Top-ups do not change effective balance + assert state.validators[validator_index].effective_balance == pre_effective_balance + else: + # new validator is added + assert len(state.validators) == pre_validator_count + 1 + assert len(state.balances) == pre_validator_count + 1 + # effective balance is set correctly + max_effective_balace = spec.get_validator_max_effective_balance(state.validators[validator_index]) + effective_balance = min(max_effective_balace, pending_deposit.amount) + effective_balance -= effective_balance % spec.EFFECTIVE_BALANCE_INCREMENT + assert state.validators[validator_index].effective_balance == effective_balance + assert get_balance(state, validator_index) == pre_balance + pending_deposit.amount + else: assert len(state.validators) == pre_validator_count assert len(state.balances) == pre_validator_count if is_top_up: assert get_balance(state, validator_index) == pre_balance - else: - if is_top_up: - # Top-ups do not change effective balance - assert state.validators[validator_index].effective_balance == pre_effective_balance - assert len(state.validators) == pre_validator_count - assert len(state.balances) == pre_validator_count - else: - # new validator is only created after the pending_deposits processing - assert len(state.validators) == pre_validator_count - assert len(state.balances) == pre_validator_count - - assert len(state.pending_deposits) == pre_pending_deposits + 1 - assert state.pending_deposits[pre_pending_deposits].pubkey == deposit_request.pubkey - assert state.pending_deposits[ - pre_pending_deposits].withdrawal_credentials == deposit_request.withdrawal_credentials - assert state.pending_deposits[pre_pending_deposits].amount == deposit_request.amount - assert state.pending_deposits[pre_pending_deposits].signature == deposit_request.signature - assert state.pending_deposits[pre_pending_deposits].slot == state.slot - - -def run_deposit_request_processing_with_specific_fork_version( - spec, - state, - fork_version, - valid=True, - effective=True): - validator_index = len(state.validators) - amount = spec.MAX_EFFECTIVE_BALANCE - - pubkey = pubkeys[validator_index] - privkey = privkeys[validator_index] - withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:] - - deposit_message = spec.DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount) - domain = spec.compute_domain(domain_type=spec.DOMAIN_DEPOSIT, fork_version=fork_version) - deposit_data = spec.DepositData( - pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount, - signature=bls.Sign(privkey, spec.compute_signing_root(deposit_message, domain)) - ) - deposit_request = spec.DepositRequest( - pubkey=deposit_data.pubkey, - withdrawal_credentials=deposit_data.withdrawal_credentials, - amount=deposit_data.amount, - signature=deposit_data.signature, - index=validator_index) - - yield from run_deposit_request_processing( - spec, - state, - deposit_request, - validator_index, - valid=valid, - effective=effective - ) + + assert len(state.pending_deposits) == 0 diff --git a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py index ff0ef2a55..382c49f51 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py +++ b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py @@ -22,7 +22,7 @@ def get_process_calls(spec): 'charge_confirmed_header_fees', # sharding 'reset_pending_headers', # sharding 'process_eth1_data_reset', - 'process_pending_balance_deposits', # electra + 'process_pending_deposits', # electra 'process_pending_consolidations', # electra 'process_effective_balance_updates', 'process_slashings_reset', @@ -38,8 +38,6 @@ def get_process_calls(spec): 'process_sync_committee_updates', # altair 'process_full_withdrawals', # capella 'process_partial_withdrawals', # capella - 'process_pending_deposits', # electra - 'process_pending_consolidations', # electra # TODO: add sharding processing functions when spec stabilizes. ] diff --git a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_effective_balance_updates.py b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_effective_balance_updates.py index 3fffcb036..50209e2e7 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_effective_balance_updates.py +++ b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_effective_balance_updates.py @@ -41,6 +41,20 @@ def run_test_effective_balance_hysteresis(spec, state, with_compounding_credenti (min, min + (hys_inc * div * 2), min + (2 * inc), "exact two step balance increment"), (min, min + (hys_inc * div * 2) + 1, min + (2 * inc), "over two steps, round down"), ] + + if with_compounding_credentials: + min = spec.MIN_ACTIVATION_BALANCE + cases = cases + [ + (min, min + (hys_inc * up), min, "bigger balance, but not high enough"), + (min, min + (hys_inc * up) + 1, min + inc, "bigger balance, high enough, but small step"), + (min, min + (hys_inc * div * 2) - 1, min + inc, "bigger balance, high enough, close to double step"), + (min, min + (hys_inc * div * 2), min + (2 * inc), "exact two step balance increment"), + (min, min + (hys_inc * div * 2) + 1, min + (2 * inc), "over two steps, round down"), + (min, min * 2 + 1, min * 2, "top up or consolidation doubling the balance"), + (min, min * 2 - 1, min * 2 - spec.EFFECTIVE_BALANCE_INCREMENT, + "top up or consolidation almost doubling the balance"), + ] + current_epoch = spec.get_current_epoch(state) for i, (pre_eff, bal, _, _) in enumerate(cases): assert spec.is_active_validator(state.validators[i], current_epoch)