Add pending_deposits queue and queue deposit requests

This commit is contained in:
Mikhail Kalinin 2024-06-20 15:48:46 +06:00
parent 3644f360b8
commit fd75470eab
14 changed files with 266 additions and 185 deletions

View File

@ -10,7 +10,7 @@ MAX_EFFECTIVE_BALANCE_ELECTRA: 2048000000000
# State list lengths
# ---------------------------------------------------------------
# `uint64(2**27)` (= 134,217,728)
PENDING_BALANCE_DEPOSITS_LIMIT: 134217728
PENDING_DEPOSITS_LIMIT: 134217728
# `uint64(2**27)` (= 134,217,728)
PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728
# `uint64(2**18)` (= 262,144)

View File

@ -10,7 +10,7 @@ MAX_EFFECTIVE_BALANCE_ELECTRA: 2048000000000
# State list lengths
# ---------------------------------------------------------------
# `uint64(2**27)` (= 134,217,728)
PENDING_BALANCE_DEPOSITS_LIMIT: 134217728
PENDING_DEPOSITS_LIMIT: 134217728
# [customized] `uint64(2**6)` (= 64)
PENDING_PARTIAL_WITHDRAWALS_LIMIT: 64
# [customized] `uint64(2**6)` (= 64)

View File

@ -25,7 +25,7 @@
- [Containers](#containers)
- [New containers](#new-containers)
- [`DepositRequest`](#depositrequest)
- [`PendingBalanceDeposit`](#pendingbalancedeposit)
- [`PendingDeposit`](#pendingdeposit)
- [`PendingPartialWithdrawal`](#pendingpartialwithdrawal)
- [`ExecutionLayerWithdrawalRequest`](#executionlayerwithdrawalrequest)
- [`ExecutionLayerConsolidationRequest`](#executionlayerconsolidationrequest)
@ -70,7 +70,7 @@
- [Epoch processing](#epoch-processing)
- [Updated `process_epoch`](#updated-process_epoch)
- [Updated `process_registry_updates`](#updated--process_registry_updates)
- [New `process_pending_balance_deposits`](#new-process_pending_balance_deposits)
- [New `process_pending_deposits`](#new-process_pending_deposits)
- [New `process_pending_consolidations`](#new-process_pending_consolidations)
- [Updated `process_effective_balance_updates`](#updated-process_effective_balance_updates)
- [Block processing](#block-processing)
@ -86,8 +86,6 @@
- [Deposits](#deposits)
- [Updated `apply_deposit`](#updated--apply_deposit)
- [New `is_valid_deposit_signature`](#new-is_valid_deposit_signature)
- [Modified `add_validator_to_registry`](#modified-add_validator_to_registry)
- [Updated `get_validator_from_deposit`](#updated-get_validator_from_deposit)
- [Voluntary exits](#voluntary-exits)
- [Updated `process_voluntary_exit`](#updated-process_voluntary_exit)
- [Execution layer withdrawal requests](#execution-layer-withdrawal-requests)
@ -156,7 +154,7 @@ The following values are (non-configurable) constants used throughout the specif
| Name | Value | Unit |
| - | - | :-: |
| `PENDING_BALANCE_DEPOSITS_LIMIT` | `uint64(2**27)` (= 134,217,728) | pending balance deposits |
| `PENDING_DEPOSITS_LIMIT` | `uint64(2**27)` (= 134,217,728) | pending deposits |
| `PENDING_PARTIAL_WITHDRAWALS_LIMIT` | `uint64(2**27)` (= 134,217,728) | pending partial withdrawals |
| `PENDING_CONSOLIDATIONS_LIMIT` | `uint64(2**18)` (= 262,144) | pending consolidations |
@ -207,14 +205,17 @@ class DepositRequest(Container):
index: uint64
```
#### `PendingBalanceDeposit`
#### `PendingDeposit`
*Note*: The container is new in EIP7251.
```python
class PendingBalanceDeposit(Container):
index: ValidatorIndex
class PendingDeposit(Container):
pubkey: BLSPubkey
withdrawal_credentials: Bytes32
amount: Gwei
signature: BLSSignature
slot: Slot
```
#### `PendingPartialWithdrawal`
@ -420,7 +421,7 @@ class BeaconState(Container):
earliest_exit_epoch: Epoch # [New in Electra:EIP7251]
consolidation_balance_to_consume: Gwei # [New in Electra:EIP7251]
earliest_consolidation_epoch: Epoch # [New in Electra:EIP7251]
pending_balance_deposits: List[PendingBalanceDeposit, PENDING_BALANCE_DEPOSITS_LIMIT] # [New in Electra:EIP7251]
pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT] # [New in Electra:EIP7251]
# [New in Electra:EIP7251]
pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT]
pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] # [New in Electra:EIP7251]
@ -655,9 +656,14 @@ def queue_excess_active_balance(state: BeaconState, index: ValidatorIndex) -> No
if balance > MIN_ACTIVATION_BALANCE:
excess_balance = balance - MIN_ACTIVATION_BALANCE
state.balances[index] = MIN_ACTIVATION_BALANCE
state.pending_balance_deposits.append(
PendingBalanceDeposit(index=index, amount=excess_balance)
)
validator = state.validators[index]
state.pending_deposits.append(PendingDeposit(
pubkey=validator.pubkey,
withdrawal_credentials=validator.withdrawal_credentials,
amount=excess_balance,
signature=bls.G2_POINT_AT_INFINITY,
slot=GENESIS_SLOT,
))
```
#### New `queue_entire_balance_and_reset_validator`
@ -668,9 +674,13 @@ def queue_entire_balance_and_reset_validator(state: BeaconState, index: Validato
validator = state.validators[index]
validator.effective_balance = 0
validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH
state.pending_balance_deposits.append(
PendingBalanceDeposit(index=index, amount=balance)
)
state.pending_deposits.append(PendingDeposit(
pubkey=validator.pubkey,
withdrawal_credentials=validator.withdrawal_credentials,
amount=balance,
signature=bls.G2_POINT_AT_INFINITY,
slot=GENESIS_SLOT,
))
```
#### New `compute_exit_epoch_and_update_churn`
@ -769,7 +779,7 @@ def process_epoch(state: BeaconState) -> None:
process_registry_updates(state) # [Modified in Electra:EIP7251]
process_slashings(state)
process_eth1_data_reset(state)
process_pending_balance_deposits(state) # [New in Electra:EIP7251]
process_pending_deposits(state) # [New in Electra:EIP7251]
process_pending_consolidations(state) # [New in Electra:EIP7251]
process_effective_balance_updates(state) # [Modified in Electra:EIP7251]
process_slashings_reset(state)
@ -804,44 +814,75 @@ def process_registry_updates(state: BeaconState) -> None:
validator.activation_epoch = activation_epoch
```
#### New `process_pending_balance_deposits`
#### New `process_pending_deposits`
```python
def process_pending_balance_deposits(state: BeaconState) -> None:
def process_pending_deposits(state: BeaconState) -> None:
available_for_processing = state.deposit_balance_to_consume + get_activation_exit_churn_limit(state)
processed_amount = 0
next_deposit_index = 0
deposits_to_postpone = []
for deposit in state.pending_balance_deposits:
validator = state.validators[deposit.index]
# Validator is exiting, postpone the deposit until after withdrawable epoch
if validator.exit_epoch < FAR_FUTURE_EPOCH:
if get_current_epoch(state) <= validator.withdrawable_epoch:
deposits_to_postpone.append(deposit)
# Deposited balance will never become active. Increase balance but do not consume churn
else:
increase_balance(state, deposit.index, deposit.amount)
# Validator is not exiting, attempt to process deposit
else:
for deposit in state.pending_deposits:
validator_pubkeys = [v.pubkey for v in state.validators]
if deposit.pubkey not in validator_pubkeys:
# Deposit does not fit in the churn, no more deposit processing in this epoch.
if processed_amount + deposit.amount > available_for_processing:
break
# Deposit fits in the churn, process it. Increase balance and consume churn.
else:
increase_balance(state, deposit.index, deposit.amount)
# Verify the deposit signature (proof of possession) which is not checked by the deposit contract
if is_valid_deposit_signature(
deposit.pubkey,
deposit.withdrawal_credentials,
deposit.amount,
deposit.signature
):
add_validator_to_registry(state, deposit.pubkey, deposit.withdrawal_credentials, deposit.amount)
# Consume churn only if signature is valid.
processed_amount += deposit.amount
else:
validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey))
validator = state.validators[validator_index]
# Validator is exiting, postpone the deposit until after withdrawable epoch
if validator.exit_epoch < FAR_FUTURE_EPOCH:
if get_current_epoch(state) <= validator.withdrawable_epoch:
deposits_to_postpone.append(deposit)
# Deposited balance will never become active. Increase balance but do not consume churn
else:
increase_balance(state, validator_index, deposit.amount)
# Validator is not exiting, attempt to process deposit
else:
# Deposit does not fit in the churn, no more deposit processing in this epoch.
if processed_amount + deposit.amount > available_for_processing:
break
# Deposit fits in the churn, process it. Increase balance and consume churn.
else:
increase_balance(state, validator_index, deposit.amount)
processed_amount += deposit.amount
# Check if valid deposit switch to compounding credentials
if (
is_compounding_withdrawal_credential(deposit.withdrawal_credentials)
and has_eth1_withdrawal_credential(validator)
and is_valid_deposit_signature(
deposit.pubkey,
deposit.withdrawal_credentials,
deposit.amount,
deposit.signature
)
):
switch_to_compounding_validator(state, validator_index)
# Regardless of how the deposit was handled, we move on in the queue.
next_deposit_index += 1
state.pending_balance_deposits = state.pending_balance_deposits[next_deposit_index:]
state.pending_deposits = state.pending_deposits[next_deposit_index:]
if len(state.pending_balance_deposits) == 0:
if len(state.pending_deposits) == 0:
state.deposit_balance_to_consume = Gwei(0)
else:
state.deposit_balance_to_consume = available_for_processing - processed_amount
state.pending_balance_deposits += deposits_to_postpone
state.pending_deposits += deposits_to_postpone
```
#### New `process_pending_consolidations`
@ -1143,21 +1184,25 @@ def apply_deposit(state: BeaconState,
if pubkey not in validator_pubkeys:
# Verify the deposit signature (proof of possession) which is not checked by the deposit contract
if is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature):
add_validator_to_registry(state, pubkey, withdrawal_credentials, amount)
add_validator_to_registry(state, pubkey, withdrawal_credentials, Gwei(0)) # [Modified in Electra:EIP7251]
# [New in Electra:EIP7251]
state.pending_deposits.append(PendingDeposit(
pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials,
amount=amount,
signature=signature,
slot=GENESIS_SLOT,
))
else:
# Increase balance by deposit amount
index = ValidatorIndex(validator_pubkeys.index(pubkey))
state.pending_balance_deposits.append(
PendingBalanceDeposit(index=index, amount=amount)
) # [Modified in Electra:EIP-7251]
# Check if valid deposit switch to compounding credentials
if (
is_compounding_withdrawal_credential(withdrawal_credentials)
and has_eth1_withdrawal_credential(state.validators[index])
and is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature)
):
switch_to_compounding_validator(state, index)
# [Modified in Electra:EIP-7251]
state.pending_deposits.append(PendingDeposit(
pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials,
amount=amount,
signature=signature,
slot=GENESIS_SLOT
))
```
###### New `is_valid_deposit_signature`
@ -1177,38 +1222,6 @@ def is_valid_deposit_signature(pubkey: BLSPubkey,
return bls.Verify(pubkey, signing_root, signature)
```
###### Modified `add_validator_to_registry`
```python
def add_validator_to_registry(state: BeaconState,
pubkey: BLSPubkey,
withdrawal_credentials: Bytes32,
amount: uint64) -> None:
index = get_index_for_new_validator(state)
validator = get_validator_from_deposit(pubkey, withdrawal_credentials)
set_or_append_list(state.validators, index, validator)
set_or_append_list(state.balances, index, 0) # [Modified in Electra:EIP7251]
set_or_append_list(state.previous_epoch_participation, index, ParticipationFlags(0b0000_0000))
set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000))
set_or_append_list(state.inactivity_scores, index, uint64(0))
state.pending_balance_deposits.append(PendingBalanceDeposit(index=index, amount=amount)) # [New in Electra:EIP7251]
```
###### Updated `get_validator_from_deposit`
```python
def get_validator_from_deposit(pubkey: BLSPubkey, withdrawal_credentials: Bytes32) -> Validator:
return Validator(
pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials,
activation_eligibility_epoch=FAR_FUTURE_EPOCH,
activation_epoch=FAR_FUTURE_EPOCH,
exit_epoch=FAR_FUTURE_EPOCH,
withdrawable_epoch=FAR_FUTURE_EPOCH,
effective_balance=0, # [Modified in Electra:EIP7251]
)
```
##### Voluntary exits
###### Updated `process_voluntary_exit`
@ -1315,13 +1328,13 @@ def process_deposit_request(state: BeaconState, deposit_request: DepositRequest)
if state.deposit_requests_start_index == UNSET_DEPOSIT_REQUESTS_START_INDEX:
state.deposit_requests_start_index = deposit_request.index
apply_deposit(
state=state,
state.pending_deposits.append(PendingDeposit(
pubkey=deposit_request.pubkey,
withdrawal_credentials=deposit_request.withdrawal_credentials,
amount=deposit_request.amount,
signature=deposit_request.signature,
)
slot=state.slot,
))
```
##### Execution layer consolidation requests
@ -1431,9 +1444,11 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32,
process_deposit(state, deposit)
# Process deposit balance updates
for deposit in state.pending_balance_deposits:
increase_balance(state, deposit.index, deposit.amount)
state.pending_balance_deposits = []
validator_pubkeys = [v.pubkey for v in state.validators]
for deposit in state.pending_deposits:
validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey))
increase_balance(state, validator_index, deposit.amount)
state.pending_deposits = []
# Process activations
for index, validator in enumerate(state.validators):

View File

@ -154,7 +154,7 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
earliest_exit_epoch=earliest_exit_epoch,
consolidation_balance_to_consume=0,
earliest_consolidation_epoch=compute_activation_exit_epoch(get_current_epoch(pre)),
pending_balance_deposits=[],
pending_deposits=[],
pending_partial_withdrawals=[],
pending_consolidations=[],
)

View File

@ -32,10 +32,13 @@ def test_success_top_up_to_withdrawn_validator(spec, state):
yield from run_deposit_processing(spec, state, deposit, validator_index)
if is_post_electra(spec):
pending_balance_deposits_len = len(state.pending_balance_deposits)
pending_balance_deposit = state.pending_balance_deposits[pending_balance_deposits_len - 1]
assert pending_balance_deposit.amount == amount
assert pending_balance_deposit.index == validator_index
pending_deposits_len = len(state.pending_deposits)
pending_deposit = state.pending_deposits[pending_deposits_len - 1]
assert pending_deposit.pubkey == deposit.data.pubkey
assert pending_deposit.withdrawal_credentials == deposit.data.withdrawal_credentials
assert pending_deposit.amount == deposit.data.amount
assert pending_deposit.signature == deposit.data.signature
assert pending_deposit.slot == spec.GENESIS_SLOT
else:
assert state.balances[validator_index] == amount
assert state.validators[validator_index].effective_balance == 0
@ -47,7 +50,7 @@ def test_success_top_up_to_withdrawn_validator(spec, state):
if is_post_electra(spec):
has_execution_withdrawal = spec.has_execution_withdrawal_credential(validator)
is_withdrawable = validator.withdrawable_epoch <= current_epoch
has_non_zero_balance = pending_balance_deposit.amount > 0
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

View File

@ -365,8 +365,11 @@ def test_top_up_and_partial_withdrawable_validator(spec, state):
yield 'post', state
if is_post_electra(spec):
assert state.pending_balance_deposits[0].amount == amount
assert state.pending_balance_deposits[0].index == validator_index
assert state.pending_deposits[0].pubkey == deposit.data.pubkey
assert state.pending_deposits[0].withdrawal_credentials == deposit.data.withdrawal_credentials
assert state.pending_deposits[0].amount == deposit.data.amount
assert state.pending_deposits[0].signature == deposit.data.signature
assert state.pending_deposits[0].slot == spec.GENESIS_SLOT
else:
# Since withdrawals happen before deposits, it becomes partially withdrawable after state transition.
validator = state.validators[validator_index]
@ -405,7 +408,7 @@ def test_top_up_to_fully_withdrawn_validator(spec, state):
balance = state.balances[validator_index]
if is_post_electra(spec):
balance += state.pending_balance_deposits[0].amount
balance += state.pending_deposits[0].amount
assert spec.is_fully_withdrawable_validator(
state.validators[validator_index],

View File

@ -122,8 +122,8 @@ def test_top_up__max_effective_balance(spec, state):
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
deposits_len = len(state.pending_balance_deposits)
assert state.pending_balance_deposits[deposits_len - 1].amount == amount
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
@ -141,8 +141,8 @@ def test_top_up__less_effective_balance(spec, state):
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
deposits_len = len(state.pending_balance_deposits)
assert state.pending_balance_deposits[deposits_len - 1].amount == amount
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
@ -161,8 +161,8 @@ def test_top_up__zero_balance(spec, state):
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
deposits_len = len(state.pending_balance_deposits)
assert state.pending_balance_deposits[deposits_len - 1].amount == amount
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
@ -276,18 +276,18 @@ def test_success_top_up_to_withdrawn_validator(spec, state):
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
deposits_len = len(state.pending_balance_deposits)
assert state.pending_balance_deposits[deposits_len - 1].amount == amount
deposits_len = len(state.pending_deposits)
assert state.pending_deposits[deposits_len - 1].amount == amount
assert state.validators[validator_index].effective_balance == 0
validator = state.validators[validator_index]
pending_balance_deposits_len = len(state.pending_balance_deposits)
pending_balance_deposit = state.pending_balance_deposits[pending_balance_deposits_len - 1]
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_balance_deposit.amount > 0
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

View File

@ -3,10 +3,13 @@ from eth2spec.test.context import (
spec_state_test,
with_electra_and_later,
)
from eth2spec.test.helpers.deposits import (
build_pending_deposit_top_up,
)
def run_process_pending_balance_deposits(spec, state):
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
def run_process_pending_deposits(spec, state):
yield from run_epoch_processing_with(spec, state, 'process_pending_deposits')
@with_electra_and_later
@ -14,17 +17,17 @@ def run_process_pending_balance_deposits(spec, state):
def test_pending_deposit_min_activation_balance(spec, state):
index = 0
amount = spec.MIN_ACTIVATION_BALANCE
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=index, amount=amount)
state.pending_deposits.append(
build_pending_deposit_top_up(spec, state, index, amount)
)
pre_balance = state.balances[index]
yield from run_process_pending_balance_deposits(spec, state)
yield from run_process_pending_deposits(spec, state)
assert state.balances[index] == pre_balance + amount
# No leftover deposit balance to consume when there are no deposits left to process
assert state.deposit_balance_to_consume == 0
assert state.pending_balance_deposits == []
assert state.pending_deposits == []
@with_electra_and_later
@ -32,16 +35,16 @@ def test_pending_deposit_min_activation_balance(spec, state):
def test_pending_deposit_balance_equal_churn(spec, state):
index = 0
amount = spec.get_activation_exit_churn_limit(state)
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=index, amount=amount)
state.pending_deposits.append(
build_pending_deposit_top_up(spec, state, index, amount)
)
pre_balance = state.balances[index]
yield from run_process_pending_balance_deposits(spec, state)
yield from run_process_pending_deposits(spec, state)
assert state.balances[index] == pre_balance + amount
assert state.deposit_balance_to_consume == 0
assert state.pending_balance_deposits == []
assert state.pending_deposits == []
@with_electra_and_later
@ -49,12 +52,12 @@ def test_pending_deposit_balance_equal_churn(spec, state):
def test_pending_deposit_balance_above_churn(spec, state):
index = 0
amount = spec.get_activation_exit_churn_limit(state) + 1
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=index, amount=amount)
state.pending_deposits.append(
build_pending_deposit_top_up(spec, state, index, amount)
)
pre_balance = state.balances[index]
yield from run_process_pending_balance_deposits(spec, state)
yield from run_process_pending_deposits(spec, state)
# deposit was above churn, balance hasn't changed
assert state.balances[index] == pre_balance
@ -63,8 +66,8 @@ def test_pending_deposit_balance_above_churn(spec, state):
state
)
# deposit is still in the queue
assert state.pending_balance_deposits == [
spec.PendingBalanceDeposit(index=index, amount=amount)
assert state.pending_deposits == [
build_pending_deposit_top_up(spec, state, index, amount)
]
@ -74,40 +77,40 @@ def test_pending_deposit_preexisting_churn(spec, state):
index = 0
amount = 10**9 + 1
state.deposit_balance_to_consume = 2 * amount
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=index, amount=amount)
state.pending_deposits.append(
build_pending_deposit_top_up(spec, state, index, amount)
)
pre_balance = state.balances[index]
yield from run_process_pending_balance_deposits(spec, state)
yield from run_process_pending_deposits(spec, state)
# balance was deposited correctly
assert state.balances[index] == pre_balance + amount
# No leftover deposit balance to consume when there are no deposits left to process
assert state.deposit_balance_to_consume == 0
# queue emptied
assert state.pending_balance_deposits == []
assert state.pending_deposits == []
@with_electra_and_later
@spec_state_test
def test_multiple_pending_deposits_below_churn(spec, state):
amount = 10**9
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=0, amount=amount)
state.pending_deposits.append(
build_pending_deposit_top_up(spec, state, validator_index=0, amount=amount)
)
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=1, amount=amount)
state.pending_deposits.append(
build_pending_deposit_top_up(spec, state, validator_index=1, amount=amount)
)
pre_balances = state.balances.copy()
yield from run_process_pending_balance_deposits(spec, state)
yield from run_process_pending_deposits(spec, state)
for i in [0, 1]:
assert state.balances[i] == pre_balances[i] + amount
# No leftover deposit balance to consume when there are no deposits left to process
assert state.deposit_balance_to_consume == 0
assert state.pending_balance_deposits == []
assert state.pending_deposits == []
@with_electra_and_later
@ -116,12 +119,12 @@ def test_multiple_pending_deposits_above_churn(spec, state):
# set third deposit to be over the churn
amount = (spec.get_activation_exit_churn_limit(state) // 3) + 1
for i in [0, 1, 2]:
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=i, amount=amount)
state.pending_deposits.append(
build_pending_deposit_top_up(spec, state, validator_index=i, amount=amount)
)
pre_balances = state.balances.copy()
yield from run_process_pending_balance_deposits(spec, state)
yield from run_process_pending_deposits(spec, state)
# First two deposits are processed, third is not because above churn
for i in [0, 1]:
@ -133,8 +136,8 @@ def test_multiple_pending_deposits_above_churn(spec, state):
== spec.get_activation_exit_churn_limit(state) - 2 * amount
)
# third deposit is still in the queue
assert state.pending_balance_deposits == [
spec.PendingBalanceDeposit(index=2, amount=amount)
assert state.pending_deposits == [
build_pending_deposit_top_up(spec, state, validator_index=2, amount=amount)
]
@ -143,20 +146,20 @@ def test_multiple_pending_deposits_above_churn(spec, state):
def test_skipped_deposit_exiting_validator(spec, state):
index = 0
amount = spec.MIN_ACTIVATION_BALANCE
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=index, amount=amount))
pre_pending_balance_deposits = state.pending_balance_deposits.copy()
state.pending_deposits.append(build_pending_deposit_top_up(spec, state, validator_index=index, amount=amount))
pre_pending_deposits = state.pending_deposits.copy()
pre_balance = state.balances[index]
# Initiate the validator's exit
spec.initiate_validator_exit(state, index)
yield from run_process_pending_balance_deposits(spec, state)
yield from run_process_pending_deposits(spec, state)
# Deposit is skipped because validator is exiting
assert state.balances[index] == pre_balance
# All deposits either processed or postponed, no leftover deposit balance to consume
assert state.deposit_balance_to_consume == 0
# The deposit is still in the queue
assert state.pending_balance_deposits == pre_pending_balance_deposits
assert state.pending_deposits == pre_pending_deposits
@with_electra_and_later
@ -165,21 +168,21 @@ def test_multiple_skipped_deposits_exiting_validators(spec, state):
amount = spec.EFFECTIVE_BALANCE_INCREMENT
for i in [0, 1, 2]:
# Append pending deposit for validator i
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount))
state.pending_deposits.append(build_pending_deposit_top_up(spec, state, validator_index=i, amount=amount))
# Initiate the exit of validator i
spec.initiate_validator_exit(state, i)
pre_pending_balance_deposits = state.pending_balance_deposits.copy()
pre_pending_deposits = state.pending_deposits.copy()
pre_balances = state.balances.copy()
yield from run_process_pending_balance_deposits(spec, state)
yield from run_process_pending_deposits(spec, state)
# All deposits are postponed, no balance changes
assert state.balances == pre_balances
# All deposits are postponed, no leftover deposit balance to consume
assert state.deposit_balance_to_consume == 0
# All deposits still in the queue, in the same order
assert state.pending_balance_deposits == pre_pending_balance_deposits
assert state.pending_deposits == pre_pending_deposits
@with_electra_and_later
@ -187,12 +190,12 @@ def test_multiple_skipped_deposits_exiting_validators(spec, state):
def test_multiple_pending_one_skipped(spec, state):
amount = spec.EFFECTIVE_BALANCE_INCREMENT
for i in [0, 1, 2]:
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount))
state.pending_deposits.append(build_pending_deposit_top_up(spec, state, validator_index=i, amount=amount))
pre_balances = state.balances.copy()
# Initiate the second validator's exit
spec.initiate_validator_exit(state, 1)
yield from run_process_pending_balance_deposits(spec, state)
yield from run_process_pending_deposits(spec, state)
# First and last deposit are processed, second is not because of exiting
for i in [0, 2]:
@ -201,7 +204,7 @@ def test_multiple_pending_one_skipped(spec, state):
# All deposits either processed or postponed, no leftover deposit balance to consume
assert state.deposit_balance_to_consume == 0
# second deposit is still in the queue
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(index=1, amount=amount)]
assert state.pending_deposits == [build_pending_deposit_top_up(spec, state, validator_index=1, amount=amount)]
@with_electra_and_later
@ -211,13 +214,13 @@ def test_mixture_of_skipped_and_above_churn(spec, state):
amount2 = spec.MAX_EFFECTIVE_BALANCE_ELECTRA
# First two validators have small deposit, third validators a large one
for i in [0, 1]:
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount01))
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=2, amount=amount2))
state.pending_deposits.append(build_pending_deposit_top_up(spec, state, validator_index=i, amount=amount01))
state.pending_deposits.append(build_pending_deposit_top_up(spec, state, validator_index=2, amount=amount2))
pre_balances = state.balances.copy()
# Initiate the second validator's exit
spec.initiate_validator_exit(state, 1)
yield from run_process_pending_balance_deposits(spec, state)
yield from run_process_pending_deposits(spec, state)
# First deposit is processed
assert state.balances[0] == pre_balances[0] + amount01
@ -228,8 +231,8 @@ def test_mixture_of_skipped_and_above_churn(spec, state):
# Deposit balance to consume is not reset because third deposit is not processed
assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(state) - amount01
# second and third deposit still in the queue, but second is appended at the end
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(index=2, amount=amount2),
spec.PendingBalanceDeposit(index=1, amount=amount01)]
assert state.pending_deposits == [build_pending_deposit_top_up(spec, state, validator_index=2, amount=amount2),
build_pending_deposit_top_up(spec, state, validator_index=1, amount=amount01)]
@with_electra_and_later
@ -237,20 +240,20 @@ def test_mixture_of_skipped_and_above_churn(spec, state):
def test_processing_deposit_of_withdrawable_validator(spec, state):
index = 0
amount = spec.MIN_ACTIVATION_BALANCE
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=index, amount=amount))
state.pending_deposits.append(build_pending_deposit_top_up(spec, state, validator_index=index, amount=amount))
pre_balance = state.balances[index]
# Initiate the validator's exit
spec.initiate_validator_exit(state, index)
# Set epoch to withdrawable epoch + 1 to allow processing of the deposit
state.slot = spec.SLOTS_PER_EPOCH * (state.validators[index].withdrawable_epoch + 1)
yield from run_process_pending_balance_deposits(spec, state)
yield from run_process_pending_deposits(spec, state)
# Deposit is correctly processed
assert state.balances[index] == pre_balance + amount
# No leftover deposit balance to consume when there are no deposits left to process
assert state.deposit_balance_to_consume == 0
assert state.pending_balance_deposits == []
assert state.pending_deposits == []
@with_electra_and_later
@ -258,7 +261,7 @@ def test_processing_deposit_of_withdrawable_validator(spec, state):
def test_processing_deposit_of_withdrawable_validator_does_not_get_churned(spec, state):
amount = spec.MAX_EFFECTIVE_BALANCE_ELECTRA
for i in [0, 1]:
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount))
state.pending_deposits.append(build_pending_deposit_top_up(spec, state, validator_index=i, amount=amount))
pre_balances = state.balances.copy()
# Initiate the first validator's exit
spec.initiate_validator_exit(state, 0)
@ -266,7 +269,7 @@ def test_processing_deposit_of_withdrawable_validator_does_not_get_churned(spec,
state.slot = spec.SLOTS_PER_EPOCH * (state.validators[0].withdrawable_epoch + 1)
# Don't use run_epoch_processing_with to avoid penalties being applied
yield 'pre', state
spec.process_pending_balance_deposits(state)
spec.process_pending_deposits(state)
yield 'post', state
# First deposit is processed though above churn limit, because validator is withdrawable
assert state.balances[0] == pre_balances[0] + amount
@ -275,4 +278,4 @@ def test_processing_deposit_of_withdrawable_validator_does_not_get_churned(spec,
# Second deposit is not processed, so there's leftover deposit balance to consume.
# First deposit does not consume any.
assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(state)
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(index=1, amount=amount)]
assert state.pending_deposits == [build_pending_deposit_top_up(spec, state, validator_index=1, amount=amount)]

View File

@ -16,7 +16,7 @@ from eth2spec.test.helpers.execution_payload import (
)
from eth2spec.test.helpers.keys import privkeys, pubkeys
from eth2spec.test.helpers.state import (
state_transition_and_sign_block
state_transition_and_sign_block,
)
@ -30,6 +30,10 @@ def run_deposit_transition_block(spec, state, block, top_up_keys=[], valid=True)
"""
yield 'pre', state
pre_pending_deposits_len = len(state.pending_deposits)
pre_validators_len = len(state.validators)
# Include deposits into a block
signed_block = state_transition_and_sign_block(spec, state, block, not valid)
yield 'blocks', [signed_block]
@ -37,12 +41,39 @@ def run_deposit_transition_block(spec, state, block, top_up_keys=[], valid=True)
# Check that deposits are applied
if valid:
expected_pubkeys = [d.data.pubkey for d in block.body.deposits]
deposit_requests = block.body.execution_payload.deposit_requests
expected_pubkeys = expected_pubkeys + [d.pubkey for d in deposit_requests if (d.pubkey not in top_up_keys)]
actual_pubkeys = [v.pubkey for v in state.validators[len(state.validators) - len(expected_pubkeys):]]
# Check that deposits are applied
for i, deposit in enumerate(block.body.deposits):
# Validator is created with 0 balance
validator = state.validators[pre_validators_len + i]
assert validator.pubkey == deposit.data.pubkey
assert validator.withdrawal_credentials == deposit.data.withdrawal_credentials
assert validator.effective_balance == spec.Gwei(0)
assert state.balances[pre_validators_len + i] == spec.Gwei(0)
assert actual_pubkeys == expected_pubkeys
# The corresponding pending deposit is created
pending_deposit = state.pending_deposits[pre_pending_deposits_len + i]
assert pending_deposit.pubkey == deposit.data.pubkey
assert pending_deposit.withdrawal_credentials == deposit.data.withdrawal_credentials
assert pending_deposit.amount == deposit.data.amount
assert pending_deposit.signature == deposit.data.signature
assert pending_deposit.slot == spec.GENESIS_SLOT
# Assert that no unexpected validators were created
assert len(state.validators) == pre_validators_len + len(block.body.deposits)
# Check that deposit requests are processed
for i, deposit_request in enumerate(block.body.execution_payload.deposit_requests):
# The corresponding pending deposit is created
pending_deposit = state.pending_deposits[pre_pending_deposits_len + len(block.body.deposits) + i]
assert pending_deposit.pubkey == deposit_request.pubkey
assert pending_deposit.withdrawal_credentials == deposit_request.withdrawal_credentials
assert pending_deposit.amount == deposit_request.amount
assert pending_deposit.signature == deposit_request.signature
assert pending_deposit.slot == signed_block.message.slot
# Assert that no unexpected pending deposits were created
assert len(state.pending_deposits) == pre_pending_deposits_len + len(
block.body.deposits) + len(block.body.execution_payload.deposit_requests)
def prepare_state_and_block(spec,
@ -222,12 +253,12 @@ def test_deposit_transition__deposit_and_top_up_same_block(spec, state):
block.body.execution_payload.deposit_requests[0].pubkey = top_up_keys[0]
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload)
pre_pending_deposits = len(state.pending_balance_deposits)
pre_pending_deposits = len(state.pending_deposits)
yield from run_deposit_transition_block(spec, state, block, top_up_keys=top_up_keys)
# Check the top up
assert len(state.pending_balance_deposits) == pre_pending_deposits + 2
assert state.pending_balance_deposits[pre_pending_deposits].amount == block.body.deposits[0].data.amount
assert len(state.pending_deposits) == pre_pending_deposits + 2
assert state.pending_deposits[pre_pending_deposits].amount == block.body.deposits[0].data.amount
amount_from_deposit = block.body.execution_payload.deposit_requests[0].amount
assert state.pending_balance_deposits[pre_pending_deposits + 1].amount == amount_from_deposit
assert state.pending_deposits[pre_pending_deposits + 1].amount == amount_from_deposit

View File

@ -219,6 +219,22 @@ def prepare_deposit_request(spec, validator_index, amount,
signed,
)
def build_pending_deposit_top_up(spec, state, validator_index, amount, slot=None):
"""
Create a pending deposit which is a top up to an existing validator
"""
if slot is None:
slot = spec.GENESIS_SLOT
return spec.PendingDeposit(
pubkey=state.validators[validator_index].pubkey,
withdrawal_credentials=state.validators[validator_index].withdrawal_credentials,
amount=amount,
signature=bls.G2_POINT_AT_INFINITY,
slot=slot,
)
#
# Run processing
#
@ -243,7 +259,7 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef
pre_effective_balance = state.validators[validator_index].effective_balance
if is_post_electra(spec):
pre_pending_deposits = len(state.pending_balance_deposits)
pre_pending_deposits = len(state.pending_deposits)
yield 'pre', state
yield 'deposit', deposit
@ -285,9 +301,13 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef
assert get_balance(state, validator_index) == pre_balance
assert state.validators[validator_index].effective_balance == pre_effective_balance
# new correct balance deposit queued up
assert len(state.pending_balance_deposits) == pre_pending_deposits + 1
assert state.pending_balance_deposits[pre_pending_deposits].amount == deposit.data.amount
assert state.pending_balance_deposits[pre_pending_deposits].index == validator_index
assert len(state.pending_deposits) == pre_pending_deposits + 1
assert state.pending_deposits[pre_pending_deposits].pubkey == deposit.data.pubkey
assert state.pending_deposits[
pre_pending_deposits].withdrawal_credentials == deposit.data.withdrawal_credentials
assert state.pending_deposits[pre_pending_deposits].amount == deposit.data.amount
assert state.pending_deposits[pre_pending_deposits].signature == deposit.data.signature
assert state.pending_deposits[pre_pending_deposits].slot == spec.GENESIS_SLOT
assert state.eth1_deposit_index == state.eth1_data.deposit_count
@ -337,7 +357,7 @@ def run_deposit_request_processing(spec, state, deposit_request, validator_index
pre_balance = get_balance(state, validator_index)
pre_effective_balance = state.validators[validator_index].effective_balance
pre_pending_deposits = len(state.pending_balance_deposits)
pre_pending_deposits = len(state.pending_deposits)
yield 'pre', state
yield 'deposit_request', deposit_request
@ -363,13 +383,17 @@ def run_deposit_request_processing(spec, state, deposit_request, validator_index
assert len(state.validators) == pre_validator_count
assert len(state.balances) == pre_validator_count
else:
# new validator
assert len(state.validators) == pre_validator_count + 1
assert len(state.balances) == pre_validator_count + 1
# 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_balance_deposits) == pre_pending_deposits + 1
assert state.pending_balance_deposits[pre_pending_deposits].amount == deposit_request.amount
assert state.pending_balance_deposits[pre_pending_deposits].index == validator_index
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(

View File

@ -36,6 +36,8 @@ 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

@ -154,7 +154,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
state.earliest_exit_epoch = spec.GENESIS_EPOCH
state.consolidation_balance_to_consume = 0
state.earliest_consolidation_epoch = 0
state.pending_balance_deposits = []
state.pending_deposits = []
state.pending_partial_withdrawals = []
state.pending_consolidations = []

View File

@ -745,7 +745,7 @@ def test_deposit_in_block(spec, state):
yield 'post', state
if is_post_electra(spec):
balance = state.pending_balance_deposits[0].amount
balance = state.pending_deposits[0].amount
else:
balance = get_balance(state, validator_index)
@ -816,7 +816,7 @@ def test_deposit_top_up(spec, state):
balance = get_balance(state, validator_index)
if is_post_electra(spec):
balance += state.pending_balance_deposits[0].amount
balance += state.pending_deposits[0].amount
assert balance == (
validator_pre_balance + amount + sync_committee_reward - sync_committee_penalty

View File

@ -39,7 +39,7 @@ if __name__ == "__main__":
_new_electra_mods = {key: 'eth2spec.test.electra.epoch_processing.test_process_' + key for key in [
'effective_balance_updates',
'pending_balance_deposits',
'pending_deposits',
'pending_consolidations',
]}
electra_mods = combine_mods(_new_electra_mods, deneb_mods)