mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-02-10 17:46:54 +00:00
Merge pull request #3979 from mkalinin/more-withdrawal-tests
eip7251: Bugfix and more withdrawal tests
This commit is contained in:
commit
c060147812
@ -1059,7 +1059,7 @@ def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal],
|
|||||||
withdrawal_index = state.next_withdrawal_index
|
withdrawal_index = state.next_withdrawal_index
|
||||||
validator_index = state.next_withdrawal_validator_index
|
validator_index = state.next_withdrawal_validator_index
|
||||||
withdrawals: List[Withdrawal] = []
|
withdrawals: List[Withdrawal] = []
|
||||||
partial_withdrawals_count = 0
|
processed_partial_withdrawals_count = 0
|
||||||
|
|
||||||
# [New in Electra:EIP7251] Consume pending partial withdrawals
|
# [New in Electra:EIP7251] Consume pending partial withdrawals
|
||||||
for withdrawal in state.pending_partial_withdrawals:
|
for withdrawal in state.pending_partial_withdrawals:
|
||||||
@ -1079,13 +1079,16 @@ def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal],
|
|||||||
))
|
))
|
||||||
withdrawal_index += WithdrawalIndex(1)
|
withdrawal_index += WithdrawalIndex(1)
|
||||||
|
|
||||||
partial_withdrawals_count += 1
|
processed_partial_withdrawals_count += 1
|
||||||
|
|
||||||
# Sweep for remaining.
|
# Sweep for remaining.
|
||||||
bound = min(len(state.validators), MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP)
|
bound = min(len(state.validators), MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP)
|
||||||
for _ in range(bound):
|
for _ in range(bound):
|
||||||
validator = state.validators[validator_index]
|
validator = state.validators[validator_index]
|
||||||
balance = state.balances[validator_index]
|
# [Modified in Electra:EIP7251]
|
||||||
|
partially_withdrawn_balance = sum(
|
||||||
|
withdrawal.amount for withdrawal in withdrawals if withdrawal.validator_index == validator_index)
|
||||||
|
balance = state.balances[validator_index] - partially_withdrawn_balance
|
||||||
if is_fully_withdrawable_validator(validator, balance, epoch):
|
if is_fully_withdrawable_validator(validator, balance, epoch):
|
||||||
withdrawals.append(Withdrawal(
|
withdrawals.append(Withdrawal(
|
||||||
index=withdrawal_index,
|
index=withdrawal_index,
|
||||||
@ -1105,7 +1108,7 @@ def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal],
|
|||||||
if len(withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
|
if len(withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
|
||||||
break
|
break
|
||||||
validator_index = ValidatorIndex((validator_index + 1) % len(state.validators))
|
validator_index = ValidatorIndex((validator_index + 1) % len(state.validators))
|
||||||
return withdrawals, partial_withdrawals_count
|
return withdrawals, processed_partial_withdrawals_count
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Modified `process_withdrawals`
|
##### Modified `process_withdrawals`
|
||||||
@ -1114,7 +1117,8 @@ def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal],
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
||||||
expected_withdrawals, partial_withdrawals_count = get_expected_withdrawals(state) # [Modified in Electra:EIP7251]
|
# [Modified in Electra:EIP7251]
|
||||||
|
expected_withdrawals, processed_partial_withdrawals_count = get_expected_withdrawals(state)
|
||||||
|
|
||||||
assert payload.withdrawals == expected_withdrawals
|
assert payload.withdrawals == expected_withdrawals
|
||||||
|
|
||||||
@ -1122,7 +1126,7 @@ def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
|||||||
decrease_balance(state, withdrawal.validator_index, withdrawal.amount)
|
decrease_balance(state, withdrawal.validator_index, withdrawal.amount)
|
||||||
|
|
||||||
# Update pending partial withdrawals [New in Electra:EIP7251]
|
# Update pending partial withdrawals [New in Electra:EIP7251]
|
||||||
state.pending_partial_withdrawals = state.pending_partial_withdrawals[partial_withdrawals_count:]
|
state.pending_partial_withdrawals = state.pending_partial_withdrawals[processed_partial_withdrawals_count:]
|
||||||
|
|
||||||
# Update the next withdrawal index if this block contained withdrawals
|
# Update the next withdrawal index if this block contained withdrawals
|
||||||
if len(expected_withdrawals) != 0:
|
if len(expected_withdrawals) != 0:
|
||||||
|
@ -11,7 +11,7 @@ from eth2spec.test.helpers.state import (
|
|||||||
next_slot,
|
next_slot,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.withdrawals import (
|
from eth2spec.test.helpers.withdrawals import (
|
||||||
prepare_expected_withdrawals_compounding,
|
prepare_expected_withdrawals,
|
||||||
run_withdrawals_processing,
|
run_withdrawals_processing,
|
||||||
set_compounding_withdrawal_credential_with_balance,
|
set_compounding_withdrawal_credential_with_balance,
|
||||||
prepare_pending_withdrawal,
|
prepare_pending_withdrawal,
|
||||||
@ -23,11 +23,11 @@ from eth2spec.test.helpers.withdrawals import (
|
|||||||
def test_success_mixed_fully_and_partial_withdrawable_compounding(spec, state):
|
def test_success_mixed_fully_and_partial_withdrawable_compounding(spec, state):
|
||||||
num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 2
|
num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 2
|
||||||
num_partial_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD - num_full_withdrawals
|
num_partial_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD - num_full_withdrawals
|
||||||
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals_compounding(
|
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals(
|
||||||
spec, state,
|
spec, state,
|
||||||
rng=random.Random(42),
|
rng=random.Random(42),
|
||||||
num_full_withdrawals=num_full_withdrawals,
|
num_full_withdrawals_comp=num_full_withdrawals,
|
||||||
num_partial_withdrawals_sweep=num_partial_withdrawals,
|
num_partial_withdrawals_comp=num_partial_withdrawals,
|
||||||
)
|
)
|
||||||
|
|
||||||
next_slot(spec, state)
|
next_slot(spec, state)
|
||||||
@ -94,14 +94,351 @@ def test_pending_withdrawals_one_skipped_one_effective(spec, state):
|
|||||||
index_0 = 3
|
index_0 = 3
|
||||||
index_1 = 5
|
index_1 = 5
|
||||||
|
|
||||||
withdrawal_0 = prepare_pending_withdrawal(spec, state, index_0)
|
pending_withdrawal_0 = prepare_pending_withdrawal(spec, state, index_0)
|
||||||
withdrawal_1 = prepare_pending_withdrawal(spec, state, index_1)
|
pending_withdrawal_1 = prepare_pending_withdrawal(spec, state, index_1)
|
||||||
|
|
||||||
# If validator doesn't have an excess balance pending withdrawal is skipped
|
# If validator doesn't have an excess balance pending withdrawal is skipped
|
||||||
state.balances[index_0] = spec.MIN_ACTIVATION_BALANCE
|
state.balances[index_0] = spec.MIN_ACTIVATION_BALANCE
|
||||||
|
|
||||||
execution_payload = build_empty_execution_payload(spec, state)
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
assert state.pending_partial_withdrawals == [withdrawal_0, withdrawal_1]
|
assert state.pending_partial_withdrawals == [pending_withdrawal_0, pending_withdrawal_1]
|
||||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
yield from run_withdrawals_processing(
|
||||||
|
spec, state,
|
||||||
|
execution_payload,
|
||||||
|
num_expected_withdrawals=1,
|
||||||
|
pending_withdrawal_requests=[pending_withdrawal_1]
|
||||||
|
)
|
||||||
|
|
||||||
assert state.pending_partial_withdrawals == []
|
assert state.pending_partial_withdrawals == []
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_pending_withdrawals_next_epoch(spec, state):
|
||||||
|
validator_index = len(state.validators) // 2
|
||||||
|
next_epoch = spec.get_current_epoch(state) + 1
|
||||||
|
|
||||||
|
pending_withdrawal = prepare_pending_withdrawal(spec, state, validator_index, withdrawable_epoch=next_epoch)
|
||||||
|
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||||
|
|
||||||
|
assert state.pending_partial_withdrawals == [pending_withdrawal]
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_pending_withdrawals_at_max(spec, state):
|
||||||
|
pending_withdrawal_requests = []
|
||||||
|
# Create spec.MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP + 1 partial withdrawals
|
||||||
|
for i in range(0, spec.MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP + 1):
|
||||||
|
pending_withdrawal = prepare_pending_withdrawal(spec, state, i)
|
||||||
|
pending_withdrawal_requests.append(pending_withdrawal)
|
||||||
|
|
||||||
|
assert len(state.pending_partial_withdrawals) == spec.MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP + 1
|
||||||
|
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
yield from run_withdrawals_processing(
|
||||||
|
spec, state,
|
||||||
|
execution_payload,
|
||||||
|
num_expected_withdrawals=spec.MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP,
|
||||||
|
pending_withdrawal_requests=pending_withdrawal_requests[:spec.MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP]
|
||||||
|
)
|
||||||
|
|
||||||
|
withdrawals_exceeding_max = pending_withdrawal_requests[spec.MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP:]
|
||||||
|
assert state.pending_partial_withdrawals == withdrawals_exceeding_max
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_pending_withdrawals_exiting_validator(spec, state):
|
||||||
|
validator_index = len(state.validators) // 2
|
||||||
|
|
||||||
|
pending_withdrawal = prepare_pending_withdrawal(spec, state, validator_index)
|
||||||
|
spec.initiate_validator_exit(state, pending_withdrawal.index)
|
||||||
|
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||||
|
|
||||||
|
assert state.pending_partial_withdrawals == []
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_pending_withdrawals_low_effective_balance(spec, state):
|
||||||
|
validator_index = len(state.validators) // 2
|
||||||
|
|
||||||
|
pending_withdrawal = prepare_pending_withdrawal(spec, state, validator_index)
|
||||||
|
state.validators[pending_withdrawal.index].effective_balance = (
|
||||||
|
spec.MIN_ACTIVATION_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
|
)
|
||||||
|
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||||
|
|
||||||
|
assert state.pending_partial_withdrawals == []
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_pending_withdrawals_no_excess_balance(spec, state):
|
||||||
|
validator_index = len(state.validators) // 2
|
||||||
|
|
||||||
|
pending_withdrawal = prepare_pending_withdrawal(spec, state, validator_index)
|
||||||
|
state.balances[pending_withdrawal.index] = spec.MIN_ACTIVATION_BALANCE
|
||||||
|
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||||
|
|
||||||
|
assert state.pending_partial_withdrawals == []
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_pending_withdrawals_with_ineffective_sweep_on_top(spec, state):
|
||||||
|
# Ensure validator will be processed by the sweep
|
||||||
|
validator_index = min(len(state.validators), spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP) // 2
|
||||||
|
|
||||||
|
pending_withdrawal = prepare_pending_withdrawal(
|
||||||
|
spec, state,
|
||||||
|
validator_index,
|
||||||
|
effective_balance=spec.MAX_EFFECTIVE_BALANCE_ELECTRA,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check that validator is partially withdrawable before pending withdrawal is processed
|
||||||
|
assert spec.is_partially_withdrawable_validator(
|
||||||
|
state.validators[validator_index],
|
||||||
|
state.balances[validator_index]
|
||||||
|
)
|
||||||
|
# And is not partially withdrawable thereafter
|
||||||
|
assert not spec.is_partially_withdrawable_validator(
|
||||||
|
state.validators[validator_index],
|
||||||
|
state.balances[validator_index] - pending_withdrawal.amount
|
||||||
|
)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
yield from run_withdrawals_processing(
|
||||||
|
spec, state,
|
||||||
|
execution_payload,
|
||||||
|
num_expected_withdrawals=1,
|
||||||
|
fully_withdrawable_indices=[],
|
||||||
|
partial_withdrawals_indices=[],
|
||||||
|
pending_withdrawal_requests=[pending_withdrawal]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert state.pending_partial_withdrawals == []
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_pending_withdrawals_with_ineffective_sweep_on_top_2(spec, state):
|
||||||
|
# Ensure validator will be processed by the sweep
|
||||||
|
validator_index = min(len(state.validators), spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP) // 2
|
||||||
|
|
||||||
|
pending_withdrawal_0 = prepare_pending_withdrawal(
|
||||||
|
spec, state,
|
||||||
|
validator_index,
|
||||||
|
effective_balance=spec.MAX_EFFECTIVE_BALANCE_ELECTRA,
|
||||||
|
amount=spec.EFFECTIVE_BALANCE_INCREMENT // 2
|
||||||
|
)
|
||||||
|
|
||||||
|
pending_withdrawal_1 = prepare_pending_withdrawal(
|
||||||
|
spec, state,
|
||||||
|
validator_index,
|
||||||
|
effective_balance=spec.MAX_EFFECTIVE_BALANCE_ELECTRA,
|
||||||
|
amount=spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set excess balance in a way that validator
|
||||||
|
# becomes not partially withdrawable only after the second pending withdrawal is processed
|
||||||
|
state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
|
assert spec.is_partially_withdrawable_validator(
|
||||||
|
state.validators[validator_index],
|
||||||
|
state.balances[validator_index] - pending_withdrawal_0.amount
|
||||||
|
)
|
||||||
|
assert not spec.is_partially_withdrawable_validator(
|
||||||
|
state.validators[validator_index],
|
||||||
|
state.balances[validator_index] - pending_withdrawal_0.amount - pending_withdrawal_1.amount
|
||||||
|
)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
yield from run_withdrawals_processing(
|
||||||
|
spec, state,
|
||||||
|
execution_payload,
|
||||||
|
num_expected_withdrawals=2,
|
||||||
|
fully_withdrawable_indices=[],
|
||||||
|
partial_withdrawals_indices=[],
|
||||||
|
pending_withdrawal_requests=[pending_withdrawal_0, pending_withdrawal_1]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert state.pending_partial_withdrawals == []
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_pending_withdrawals_with_effective_sweep_on_top(spec, state):
|
||||||
|
# Ensure validator will be processed by the sweep
|
||||||
|
validator_index = min(len(state.validators), spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP) // 2
|
||||||
|
|
||||||
|
pending_withdrawal_0 = prepare_pending_withdrawal(
|
||||||
|
spec, state,
|
||||||
|
validator_index,
|
||||||
|
effective_balance=spec.MAX_EFFECTIVE_BALANCE_ELECTRA,
|
||||||
|
amount=spec.EFFECTIVE_BALANCE_INCREMENT // 2
|
||||||
|
)
|
||||||
|
|
||||||
|
pending_withdrawal_1 = prepare_pending_withdrawal(
|
||||||
|
spec, state,
|
||||||
|
validator_index,
|
||||||
|
effective_balance=spec.MAX_EFFECTIVE_BALANCE_ELECTRA,
|
||||||
|
amount=spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set excess balance to requested amount times three,
|
||||||
|
# so the validator is partially withdrawable after pending withdrawal is processed
|
||||||
|
state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + spec.EFFECTIVE_BALANCE_INCREMENT * 2
|
||||||
|
assert spec.is_partially_withdrawable_validator(
|
||||||
|
state.validators[validator_index],
|
||||||
|
state.balances[validator_index] - pending_withdrawal_0.amount - pending_withdrawal_1.amount
|
||||||
|
)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
yield from run_withdrawals_processing(
|
||||||
|
spec, state,
|
||||||
|
execution_payload,
|
||||||
|
num_expected_withdrawals=3,
|
||||||
|
fully_withdrawable_indices=[],
|
||||||
|
partial_withdrawals_indices=[validator_index],
|
||||||
|
pending_withdrawal_requests=[pending_withdrawal_0, pending_withdrawal_1]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert state.pending_partial_withdrawals == []
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_pending_withdrawals_with_sweep_different_validator(spec, state):
|
||||||
|
# Ensure validator will be processed by the sweep
|
||||||
|
validator_index_0 = min(len(state.validators), spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP) // 2 - 1
|
||||||
|
validator_index_1 = min(len(state.validators), spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP) // 2
|
||||||
|
|
||||||
|
# Initiate pending withdrawal for the first validator
|
||||||
|
pending_withdrawal_0 = prepare_pending_withdrawal(
|
||||||
|
spec, state,
|
||||||
|
validator_index_0,
|
||||||
|
effective_balance=spec.MAX_EFFECTIVE_BALANCE_ELECTRA,
|
||||||
|
amount=spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make the second validator partially withdrawable by the sweep
|
||||||
|
set_compounding_withdrawal_credential_with_balance(
|
||||||
|
spec, state, validator_index_1,
|
||||||
|
effective_balance=spec.MAX_EFFECTIVE_BALANCE_ELECTRA,
|
||||||
|
balance=(spec.MAX_EFFECTIVE_BALANCE_ELECTRA + spec.EFFECTIVE_BALANCE_INCREMENT)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert spec.is_partially_withdrawable_validator(
|
||||||
|
state.validators[validator_index_1],
|
||||||
|
state.balances[validator_index_1]
|
||||||
|
)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
yield from run_withdrawals_processing(
|
||||||
|
spec, state,
|
||||||
|
execution_payload,
|
||||||
|
num_expected_withdrawals=2,
|
||||||
|
fully_withdrawable_indices=[],
|
||||||
|
partial_withdrawals_indices=[validator_index_1],
|
||||||
|
pending_withdrawal_requests=[pending_withdrawal_0]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert state.pending_partial_withdrawals == []
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_pending_withdrawals_mixed_with_sweep_and_fully_withdrawable(spec, state):
|
||||||
|
num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 4
|
||||||
|
num_partial_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 4
|
||||||
|
num_full_withdrawals_comp = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 4
|
||||||
|
num_partial_withdrawals_comp = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 4
|
||||||
|
num_pending_withdrawal_requests = spec.MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP // 2
|
||||||
|
|
||||||
|
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals(
|
||||||
|
spec, state,
|
||||||
|
rng=random.Random(42),
|
||||||
|
num_full_withdrawals=num_full_withdrawals,
|
||||||
|
num_partial_withdrawals=num_partial_withdrawals,
|
||||||
|
num_full_withdrawals_comp=num_full_withdrawals_comp,
|
||||||
|
num_partial_withdrawals_comp=num_partial_withdrawals_comp,
|
||||||
|
)
|
||||||
|
|
||||||
|
pending_withdrawal_requests = []
|
||||||
|
for index in range(0, len(state.validators)):
|
||||||
|
if len(pending_withdrawal_requests) >= num_pending_withdrawal_requests:
|
||||||
|
break
|
||||||
|
if index in (fully_withdrawable_indices + partial_withdrawals_indices):
|
||||||
|
continue
|
||||||
|
|
||||||
|
pending_withdrawal = prepare_pending_withdrawal(spec, state, index)
|
||||||
|
pending_withdrawal_requests.append(pending_withdrawal)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
yield from run_withdrawals_processing(
|
||||||
|
spec, state,
|
||||||
|
execution_payload,
|
||||||
|
num_expected_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD,
|
||||||
|
fully_withdrawable_indices=fully_withdrawable_indices,
|
||||||
|
partial_withdrawals_indices=partial_withdrawals_indices,
|
||||||
|
pending_withdrawal_requests=pending_withdrawal_requests
|
||||||
|
)
|
||||||
|
|
||||||
|
assert state.pending_partial_withdrawals == []
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_pending_withdrawals_at_max_mixed_with_sweep_and_fully_withdrawable(spec, state):
|
||||||
|
num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 4
|
||||||
|
num_partial_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 4
|
||||||
|
num_full_withdrawals_comp = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 4
|
||||||
|
num_partial_withdrawals_comp = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 4
|
||||||
|
num_pending_withdrawal_requests = spec.MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP + 1
|
||||||
|
|
||||||
|
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals(
|
||||||
|
spec, state,
|
||||||
|
rng=random.Random(42),
|
||||||
|
num_full_withdrawals=num_full_withdrawals,
|
||||||
|
num_partial_withdrawals=num_partial_withdrawals,
|
||||||
|
num_full_withdrawals_comp=num_full_withdrawals_comp,
|
||||||
|
num_partial_withdrawals_comp=num_partial_withdrawals_comp,
|
||||||
|
)
|
||||||
|
|
||||||
|
pending_withdrawal_requests = []
|
||||||
|
for index in range(0, len(state.validators)):
|
||||||
|
if len(pending_withdrawal_requests) >= num_pending_withdrawal_requests:
|
||||||
|
break
|
||||||
|
if index in (fully_withdrawable_indices + partial_withdrawals_indices):
|
||||||
|
continue
|
||||||
|
|
||||||
|
pending_withdrawal = prepare_pending_withdrawal(spec, state, index)
|
||||||
|
pending_withdrawal_requests.append(pending_withdrawal)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
yield from run_withdrawals_processing(
|
||||||
|
spec, state,
|
||||||
|
execution_payload,
|
||||||
|
num_expected_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD,
|
||||||
|
fully_withdrawable_indices=fully_withdrawable_indices,
|
||||||
|
partial_withdrawals_indices=partial_withdrawals_indices,
|
||||||
|
pending_withdrawal_requests=pending_withdrawal_requests[:spec.MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP]
|
||||||
|
)
|
||||||
|
|
||||||
|
withdrawals_exceeding_max = pending_withdrawal_requests[spec.MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP:]
|
||||||
|
assert state.pending_partial_withdrawals == withdrawals_exceeding_max
|
||||||
|
@ -63,11 +63,21 @@ def sample_withdrawal_indices(spec, state, rng, num_full_withdrawals, num_partia
|
|||||||
|
|
||||||
|
|
||||||
def prepare_expected_withdrawals(spec, state, rng,
|
def prepare_expected_withdrawals(spec, state, rng,
|
||||||
num_full_withdrawals=0, num_partial_withdrawals=0):
|
num_full_withdrawals=0, num_partial_withdrawals=0,
|
||||||
|
num_full_withdrawals_comp=0, num_partial_withdrawals_comp=0):
|
||||||
fully_withdrawable_indices, partial_withdrawals_indices = sample_withdrawal_indices(
|
fully_withdrawable_indices, partial_withdrawals_indices = sample_withdrawal_indices(
|
||||||
spec, state, rng, num_full_withdrawals, num_partial_withdrawals
|
spec, state, rng,
|
||||||
|
num_full_withdrawals + num_full_withdrawals_comp,
|
||||||
|
num_partial_withdrawals + num_partial_withdrawals_comp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fully_withdrawable_indices_comp = rng.sample(fully_withdrawable_indices, num_full_withdrawals_comp)
|
||||||
|
partial_withdrawals_indices_comp = rng.sample(partial_withdrawals_indices, num_partial_withdrawals_comp)
|
||||||
|
|
||||||
|
for index in (fully_withdrawable_indices_comp + partial_withdrawals_indices_comp):
|
||||||
|
address = state.validators[index].withdrawal_credentials[12:]
|
||||||
|
set_compounding_withdrawal_credential_with_balance(spec, state, index, address=address)
|
||||||
|
|
||||||
for index in fully_withdrawable_indices:
|
for index in fully_withdrawable_indices:
|
||||||
set_validator_fully_withdrawable(spec, state, index)
|
set_validator_fully_withdrawable(spec, state, index)
|
||||||
for index in partial_withdrawals_indices:
|
for index in partial_withdrawals_indices:
|
||||||
@ -97,32 +107,13 @@ def set_compounding_withdrawal_credential_with_balance(spec, state, index,
|
|||||||
state.balances[index] = balance
|
state.balances[index] = balance
|
||||||
|
|
||||||
|
|
||||||
def prepare_expected_withdrawals_compounding(spec, state, rng,
|
|
||||||
num_full_withdrawals=0,
|
|
||||||
num_partial_withdrawals_sweep=0,
|
|
||||||
excess_balance=1000000000):
|
|
||||||
assert is_post_electra(spec)
|
|
||||||
|
|
||||||
fully_withdrawable_indices, partial_withdrawals_sweep_indices = sample_withdrawal_indices(
|
|
||||||
spec, state, rng, num_full_withdrawals, num_partial_withdrawals_sweep
|
|
||||||
)
|
|
||||||
|
|
||||||
for index in fully_withdrawable_indices + partial_withdrawals_sweep_indices:
|
|
||||||
address = state.validators[index].withdrawal_credentials[12:]
|
|
||||||
set_compounding_withdrawal_credential_with_balance(spec, state, index, address=address)
|
|
||||||
|
|
||||||
for index in fully_withdrawable_indices:
|
|
||||||
set_validator_fully_withdrawable(spec, state, index)
|
|
||||||
for index in partial_withdrawals_sweep_indices:
|
|
||||||
set_validator_partially_withdrawable(spec, state, index)
|
|
||||||
|
|
||||||
return fully_withdrawable_indices, partial_withdrawals_sweep_indices
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_pending_withdrawal(spec, state, validator_index,
|
def prepare_pending_withdrawal(spec, state, validator_index,
|
||||||
effective_balance=32_000_000_000, amount=1_000_000_000):
|
effective_balance=32_000_000_000, amount=1_000_000_000, withdrawable_epoch=None):
|
||||||
assert is_post_electra(spec)
|
assert is_post_electra(spec)
|
||||||
|
|
||||||
|
if withdrawable_epoch is None:
|
||||||
|
withdrawable_epoch = spec.get_current_epoch(state)
|
||||||
|
|
||||||
balance = effective_balance + amount
|
balance = effective_balance + amount
|
||||||
set_compounding_withdrawal_credential_with_balance(
|
set_compounding_withdrawal_credential_with_balance(
|
||||||
spec, state, validator_index, effective_balance, balance
|
spec, state, validator_index, effective_balance, balance
|
||||||
@ -131,7 +122,7 @@ def prepare_pending_withdrawal(spec, state, validator_index,
|
|||||||
withdrawal = spec.PendingPartialWithdrawal(
|
withdrawal = spec.PendingPartialWithdrawal(
|
||||||
index=validator_index,
|
index=validator_index,
|
||||||
amount=amount,
|
amount=amount,
|
||||||
withdrawable_epoch=spec.get_current_epoch(state),
|
withdrawable_epoch=withdrawable_epoch,
|
||||||
)
|
)
|
||||||
state.pending_partial_withdrawals.append(withdrawal)
|
state.pending_partial_withdrawals.append(withdrawal)
|
||||||
|
|
||||||
@ -175,7 +166,8 @@ def verify_post_state(state, spec, expected_withdrawals,
|
|||||||
|
|
||||||
|
|
||||||
def run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=None,
|
def run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=None,
|
||||||
fully_withdrawable_indices=None, partial_withdrawals_indices=None, valid=True):
|
fully_withdrawable_indices=None, partial_withdrawals_indices=None,
|
||||||
|
pending_withdrawal_requests=None, valid=True):
|
||||||
"""
|
"""
|
||||||
Run ``process_withdrawals``, yielding:
|
Run ``process_withdrawals``, yielding:
|
||||||
- pre-state ('pre')
|
- pre-state ('pre')
|
||||||
@ -206,6 +198,11 @@ def run_withdrawals_processing(spec, state, execution_payload, num_expected_with
|
|||||||
|
|
||||||
yield 'post', state
|
yield 'post', state
|
||||||
|
|
||||||
|
# Check withdrawal indices
|
||||||
|
assert state.next_withdrawal_index == pre_state.next_withdrawal_index + len(expected_withdrawals)
|
||||||
|
for index, withdrawal in enumerate(execution_payload.withdrawals):
|
||||||
|
assert withdrawal.index == pre_state.next_withdrawal_index + index
|
||||||
|
|
||||||
if len(expected_withdrawals) == 0:
|
if len(expected_withdrawals) == 0:
|
||||||
next_withdrawal_validator_index = (
|
next_withdrawal_validator_index = (
|
||||||
pre_state.next_withdrawal_validator_index + spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP
|
pre_state.next_withdrawal_validator_index + spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP
|
||||||
@ -220,4 +217,12 @@ def run_withdrawals_processing(spec, state, execution_payload, num_expected_with
|
|||||||
if fully_withdrawable_indices is not None or partial_withdrawals_indices is not None:
|
if fully_withdrawable_indices is not None or partial_withdrawals_indices is not None:
|
||||||
verify_post_state(state, spec, expected_withdrawals, fully_withdrawable_indices, partial_withdrawals_indices)
|
verify_post_state(state, spec, expected_withdrawals, fully_withdrawable_indices, partial_withdrawals_indices)
|
||||||
|
|
||||||
|
# Check withdrawal requests
|
||||||
|
if pending_withdrawal_requests is not None:
|
||||||
|
assert len(pending_withdrawal_requests) <= len(execution_payload.withdrawals)
|
||||||
|
for index, request in enumerate(pending_withdrawal_requests):
|
||||||
|
withdrawal = execution_payload.withdrawals[index]
|
||||||
|
assert withdrawal.validator_index == request.index
|
||||||
|
assert withdrawal.amount == request.amount
|
||||||
|
|
||||||
return expected_withdrawals
|
return expected_withdrawals
|
||||||
|
Loading…
x
Reference in New Issue
Block a user