Add more deposit tests

This commit is contained in:
Mikhail Kalinin 2024-08-26 16:18:53 +06:00
parent 97e6166bf1
commit 990de16c0f
5 changed files with 932 additions and 557 deletions

View File

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

View File

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

View File

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

View File

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

View File

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