From fd75470eab3dec484450ba82092df27633a4068d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 20 Jun 2024 15:48:46 +0600 Subject: [PATCH 001/103] Add pending_deposits queue and queue deposit requests --- presets/mainnet/electra.yaml | 2 +- presets/minimal/electra.yaml | 2 +- specs/electra/beacon-chain.md | 187 ++++++++++-------- specs/electra/fork.md | 2 +- .../block_processing/test_process_deposit.py | 13 +- .../test/capella/sanity/test_blocks.py | 9 +- .../test_process_deposit_request.py | 22 +-- ...ts.py => test_process_pending_deposits.py} | 107 +++++----- .../sanity/blocks/test_deposit_transition.py | 53 +++-- .../pyspec/eth2spec/test/helpers/deposits.py | 46 +++-- .../eth2spec/test/helpers/epoch_processing.py | 2 + .../pyspec/eth2spec/test/helpers/genesis.py | 2 +- .../test/phase0/sanity/test_blocks.py | 4 +- tests/generators/epoch_processing/main.py | 2 +- 14 files changed, 267 insertions(+), 186 deletions(-) rename tests/core/pyspec/eth2spec/test/electra/epoch_processing/{test_process_pending_balance_deposits.py => test_process_pending_deposits.py} (68%) diff --git a/presets/mainnet/electra.yaml b/presets/mainnet/electra.yaml index bb97cfa445..4d54750d11 100644 --- a/presets/mainnet/electra.yaml +++ b/presets/mainnet/electra.yaml @@ -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) diff --git a/presets/minimal/electra.yaml b/presets/minimal/electra.yaml index ef1ce494d8..f6183f9565 100644 --- a/presets/minimal/electra.yaml +++ b/presets/minimal/electra.yaml @@ -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) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 7b75667eb1..d65aa29441 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -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): diff --git a/specs/electra/fork.md b/specs/electra/fork.md index fab02ddf25..8130d6c7d3 100644 --- a/specs/electra/fork.md +++ b/specs/electra/fork.md @@ -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=[], ) diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py index 4f2176ca26..1abb93e62c 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py @@ -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 diff --git a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py index dabc2be18b..406fde341f 100644 --- a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py @@ -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], diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py index 4b96200e45..737fbcabb1 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py @@ -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 diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_balance_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py similarity index 68% rename from tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_balance_deposits.py rename to tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index c5789e8090..1318f7f6f2 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_balance_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -3,10 +3,13 @@ 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)] diff --git a/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_deposit_transition.py b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_deposit_transition.py index f253b6c60d..f79d7d2aca 100644 --- a/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_deposit_transition.py +++ b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_deposit_transition.py @@ -16,7 +16,7 @@ ) 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):]] - - assert actual_pubkeys == 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) + + # 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 diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index c8aa30313c..ae50f6b38a 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -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( diff --git a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py index 44b42aff91..6b44e22c5d 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py +++ b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py @@ -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. ] diff --git a/tests/core/pyspec/eth2spec/test/helpers/genesis.py b/tests/core/pyspec/eth2spec/test/helpers/genesis.py index 34ddd88ca2..dbbc6f5b5e 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/genesis.py +++ b/tests/core/pyspec/eth2spec/test/helpers/genesis.py @@ -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 = [] diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py index 41ba2e8b71..06d87b6720 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -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 diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index 3796f76a97..6e7c3e376e 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -39,7 +39,7 @@ _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) From 2a202c8ecbe13da39cd52ed9627993e46bac3c0d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 20 Jun 2024 18:46:38 +0600 Subject: [PATCH 002/103] Refactor pending deposit processing --- specs/electra/beacon-chain.md | 68 ++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index d65aa29441..0cbb5da501 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -58,6 +58,7 @@ - [New `get_active_balance`](#new-get_active_balance) - [New `get_pending_balance_to_withdraw`](#new-get_pending_balance_to_withdraw) - [Modified `get_attesting_indices`](#modified-get_attesting_indices) + - [`get_activation_churn_consumption`](#get_activation_churn_consumption) - [Beacon state mutators](#beacon-state-mutators) - [Updated `initiate_validator_exit`](#updated--initiate_validator_exit) - [New `switch_to_compounding_validator`](#new-switch_to_compounding_validator) @@ -616,6 +617,27 @@ def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[V return output ``` +#### `get_activation_churn_consumption` + +```python +def get_activation_churn_consumption(state: BeaconState, pending_deposit: PendingDeposit) -> Gwei: + """ + Return amount of activation churn consumed by the ``pending_deposit``. + """ + validator_pubkeys = [v.pubkey for v in state.validators] + + if pending_deposit.pubkey not in validator_pubkeys: + return pending_deposit.amount + else: + validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey)) + validator = state.validators[validator_index] + # Validator is exiting, do not consume the churn + if validator.exit_epoch < FAR_FUTURE_EPOCH: + return Gwei(0) + else: + return pending_deposit.amount +``` + ### Beacon state mutators #### Updated `initiate_validator_exit` @@ -824,12 +846,17 @@ def process_pending_deposits(state: BeaconState) -> None: deposits_to_postpone = [] for deposit in state.pending_deposits: + # If deposit does not fit in the churn, do no more deposit processing in this epoch. + churn_consumption = get_activation_churn_consumption(state, deposit) + if processed_amount + churn_consumption > available_for_processing: + break + + # Consume churn and process deposit + processed_amount += churn_consumption + 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 # Verify the deposit signature (proof of possession) which is not checked by the deposit contract if is_valid_deposit_signature( deposit.pubkey, @@ -838,8 +865,6 @@ def process_pending_deposits(state: BeaconState) -> None: 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] @@ -852,25 +877,20 @@ def process_pending_deposits(state: BeaconState) -> None: 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) + # Increase balance + increase_balance(state, validator_index, 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 From 0b7fef37b3b1114d91564a317164f61fb5c72867 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 20 Jun 2024 19:09:41 +0600 Subject: [PATCH 003/103] Limit number of pending deposits to be processed per epoch --- presets/mainnet/electra.yaml | 5 +++++ presets/minimal/electra.yaml | 5 +++++ specs/electra/beacon-chain.md | 12 +++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/presets/mainnet/electra.yaml b/presets/mainnet/electra.yaml index 4d54750d11..7b336c216c 100644 --- a/presets/mainnet/electra.yaml +++ b/presets/mainnet/electra.yaml @@ -43,3 +43,8 @@ MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 16 # --------------------------------------------------------------- # 2**3 ( = 8) pending withdrawals MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 8 + +# Pending deposits processing +# --------------------------------------------------------------- +# 2**4 ( = 4) pending deposits +MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: 16 diff --git a/presets/minimal/electra.yaml b/presets/minimal/electra.yaml index f6183f9565..cbcb754e9c 100644 --- a/presets/minimal/electra.yaml +++ b/presets/minimal/electra.yaml @@ -43,3 +43,8 @@ MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 2 # --------------------------------------------------------------- # 2**0 ( = 1) pending withdrawals MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 1 + +# Pending deposits processing +# --------------------------------------------------------------- +# 2**4 ( = 4) pending deposits +MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: 16 diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 0cbb5da501..e87ca50032 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -20,6 +20,7 @@ - [Max operations per block](#max-operations-per-block) - [Execution](#execution) - [Withdrawals processing](#withdrawals-processing) + - [Pending deposits processing](#pending-deposits-processing) - [Configuration](#configuration) - [Validator cycle](#validator-cycle) - [Containers](#containers) @@ -180,6 +181,11 @@ The following values are (non-configurable) constants used throughout the specif | - | - | - | | `MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP` | `uint64(2**3)` (= 8)| *[New in Electra:EIP7002]* Maximum number of pending partial withdrawals to process per payload | +### Pending deposits processing +| Name | Value | Description | +| - | - | - | +| `MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING` | `uint64(2**4)` (= 16)| *[New in Electra:EIP6110]* Maximum number of pending deposits to process per epoch | + ## Configuration ### Validator cycle @@ -846,7 +852,11 @@ def process_pending_deposits(state: BeaconState) -> None: deposits_to_postpone = [] for deposit in state.pending_deposits: - # If deposit does not fit in the churn, do no more deposit processing in this epoch. + # Check if number of processed deposits fits in the limit + if next_deposit_index > MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: + break + + # Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. churn_consumption = get_activation_churn_consumption(state, deposit) if processed_amount + churn_consumption > available_for_processing: break From 5c3a51ae794f1d61c9dbc5f9e64c790ea5b077ba Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 25 Jun 2024 17:26:17 +0600 Subject: [PATCH 004/103] Process deposit requests after Eth1 bridge deposits are applied --- specs/electra/beacon-chain.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index e87ca50032..e096eaf307 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -635,7 +635,7 @@ def get_activation_churn_consumption(state: BeaconState, pending_deposit: Pendin if pending_deposit.pubkey not in validator_pubkeys: return pending_deposit.amount else: - validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey)) + validator_index = ValidatorIndex(validator_pubkeys.index(pending_deposit.pubkey)) validator = state.validators[validator_index] # Validator is exiting, do not consume the churn if validator.exit_epoch < FAR_FUTURE_EPOCH: @@ -850,8 +850,15 @@ def process_pending_deposits(state: BeaconState) -> None: processed_amount = 0 next_deposit_index = 0 deposits_to_postpone = [] + is_churn_limit_reached = False for deposit in state.pending_deposits: + # Do not process any deposit requests if Eth1 bridge deposits are not yet applied + if (state.deposit_requests_start_index != UNSET_DEPOSIT_REQUESTS_START_INDEX + and state.eth1_deposit_index < state.deposit_requests_start_index + and deposit.slot > GENESIS_SLOT): + break + # Check if number of processed deposits fits in the limit if next_deposit_index > MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: break @@ -859,6 +866,7 @@ def process_pending_deposits(state: BeaconState) -> None: # Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. churn_consumption = get_activation_churn_consumption(state, deposit) if processed_amount + churn_consumption > available_for_processing: + is_churn_limit_reached = True break # Consume churn and process deposit @@ -907,10 +915,10 @@ def process_pending_deposits(state: BeaconState) -> None: state.pending_deposits = state.pending_deposits[next_deposit_index:] - if len(state.pending_deposits) == 0: - state.deposit_balance_to_consume = Gwei(0) - else: + if is_churn_limit_reached: state.deposit_balance_to_consume = available_for_processing - processed_amount + else: + state.deposit_balance_to_consume = Gwei(0) state.pending_deposits += deposits_to_postpone ``` From 179ee8ee7888c00e78c6f75e1f002bb876884ce5 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 25 Jun 2024 17:47:18 +0600 Subject: [PATCH 005/103] Finalize deposit requests before processing --- specs/electra/beacon-chain.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index e096eaf307..6544b49368 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -851,15 +851,19 @@ def process_pending_deposits(state: BeaconState) -> None: next_deposit_index = 0 deposits_to_postpone = [] is_churn_limit_reached = False + finalized_slot = compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) for deposit in state.pending_deposits: - # Do not process any deposit requests if Eth1 bridge deposits are not yet applied - if (state.deposit_requests_start_index != UNSET_DEPOSIT_REQUESTS_START_INDEX - and state.eth1_deposit_index < state.deposit_requests_start_index - and deposit.slot > GENESIS_SLOT): + # Do not process a deposit request if Eth1 bridge deposits are not yet applied. + is_deposit_request = deposit.slot > GENESIS_SLOT + if is_deposit_request and state.eth1_deposit_index < state.deposit_requests_start_index: break - # Check if number of processed deposits fits in the limit + # Check if deposit has been finalized, otherwise, stop processing. + if deposit.slot > finalized_slot: + break + + # Check if number of processed deposits fits in the limit, otherwise, stop processing. if next_deposit_index > MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: break From 43e7344143f30a9815a8c8d1698b653beb6be375 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 26 Jun 2024 13:42:48 +0600 Subject: [PATCH 006/103] Refactor process_pending_deposits --- specs/electra/beacon-chain.md | 124 +++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 48 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 6544b49368..4e5f351f3f 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -72,6 +72,7 @@ - [Epoch processing](#epoch-processing) - [Updated `process_epoch`](#updated-process_epoch) - [Updated `process_registry_updates`](#updated--process_registry_updates) + - [New `apply_pending_deposit`](#new-apply_pending_deposit) - [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) @@ -626,22 +627,22 @@ def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[V #### `get_activation_churn_consumption` ```python -def get_activation_churn_consumption(state: BeaconState, pending_deposit: PendingDeposit) -> Gwei: +def get_activation_churn_consumption(state: BeaconState, deposit: PendingDeposit) -> Gwei: """ - Return amount of activation churn consumed by the ``pending_deposit``. + Return amount of activation churn consumed by the ``deposit``. """ validator_pubkeys = [v.pubkey for v in state.validators] - if pending_deposit.pubkey not in validator_pubkeys: - return pending_deposit.amount + if deposit.pubkey not in validator_pubkeys: + return deposit.amount else: - validator_index = ValidatorIndex(validator_pubkeys.index(pending_deposit.pubkey)) + validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey)) validator = state.validators[validator_index] # Validator is exiting, do not consume the churn if validator.exit_epoch < FAR_FUTURE_EPOCH: return Gwei(0) else: - return pending_deposit.amount + return deposit.amount ``` ### Beacon state mutators @@ -842,8 +843,63 @@ def process_registry_updates(state: BeaconState) -> None: validator.activation_epoch = activation_epoch ``` +#### New `apply_pending_deposit` + +```python +def apply_pending_deposit(state: BeaconState, deposit: PendingDeposit) -> bool: + """ + Applies ``deposit`` to the ``state``. + Returns ``True`` if ``deposit`` has been successfully applied + and can be removed from the queue. + """ + validator_pubkeys = [v.pubkey for v in state.validators] + if deposit.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( + deposit.pubkey, + deposit.withdrawal_credentials, + deposit.amount, + deposit.signature + ): + add_validator_to_registry(state, deposit.pubkey, deposit.withdrawal_credentials, 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: + return False + # 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: + # Increase balance + increase_balance(state, validator_index, 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) + + return True +``` + #### New `process_pending_deposits` +Iterating over `pending_deposits` queue this function runs the following checks before applying pending deposit: +1. All Eth1 bridge deposits are processed before the first deposit request gets processed. +2. Deposit position in the queue is finalized. +3. Deposit does not exceed the `MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING` limit. +4. Deposit does not exceed the activation churn limit. + ```python def process_pending_deposits(state: BeaconState) -> None: available_for_processing = state.deposit_balance_to_consume + get_activation_exit_churn_limit(state) @@ -854,16 +910,20 @@ def process_pending_deposits(state: BeaconState) -> None: finalized_slot = compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) for deposit in state.pending_deposits: - # Do not process a deposit request if Eth1 bridge deposits are not yet applied. - is_deposit_request = deposit.slot > GENESIS_SLOT - if is_deposit_request and state.eth1_deposit_index < state.deposit_requests_start_index: + # Do not process deposit requests if Eth1 bridge deposits are not yet applied. + if ( + # Is deposit request + deposit.slot > GENESIS_SLOT and + # There are pending Eth1 bridge deposits + state.eth1_deposit_index < state.deposit_requests_start_index + ): break # Check if deposit has been finalized, otherwise, stop processing. if deposit.slot > finalized_slot: break - # Check if number of processed deposits fits in the limit, otherwise, stop processing. + # Check if number of processed deposits has not reached the limit, otherwise, stop processing. if next_deposit_index > MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: break @@ -873,52 +933,20 @@ def process_pending_deposits(state: BeaconState) -> None: is_churn_limit_reached = True break - # Consume churn and process deposit + # Consume churn and apply deposit processed_amount += churn_consumption + is_deposit_applied = apply_pending_deposit(state, deposit) - validator_pubkeys = [v.pubkey for v in state.validators] - - if deposit.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( - deposit.pubkey, - deposit.withdrawal_credentials, - deposit.amount, - deposit.signature - ): - add_validator_to_registry(state, deposit.pubkey, deposit.withdrawal_credentials, 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: - # Increase balance - increase_balance(state, validator_index, 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) + # Postpone deposit if it has not been applied. + if not is_deposit_applied: + deposits_to_postpone.append(deposit) # Regardless of how the deposit was handled, we move on in the queue. next_deposit_index += 1 state.pending_deposits = state.pending_deposits[next_deposit_index:] + # Accumulate churn only if the churn limit has been hit. if is_churn_limit_reached: state.deposit_balance_to_consume = available_for_processing - processed_amount else: From e1a4d8b60e245984bc157875794c912b4b60db37 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 1 Jul 2024 15:26:56 -0500 Subject: [PATCH 007/103] added some simple tests --- specs/electra/beacon-chain.md | 186 +++++++++--------- .../test_process_pending_deposits.py | 39 ++++ 2 files changed, 133 insertions(+), 92 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 83dd728a77..c903452159 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -8,97 +8,99 @@ -- [Introduction](#introduction) -- [Constants](#constants) - - [Misc](#misc) - - [Withdrawal prefixes](#withdrawal-prefixes) - - [Domains](#domains) -- [Preset](#preset) - - [Gwei values](#gwei-values) - - [Rewards and penalties](#rewards-and-penalties) - - [State list lengths](#state-list-lengths) - - [Max operations per block](#max-operations-per-block) - - [Execution](#execution) - - [Withdrawals processing](#withdrawals-processing) - - [Pending deposits processing](#pending-deposits-processing) -- [Configuration](#configuration) - - [Validator cycle](#validator-cycle) -- [Containers](#containers) - - [New containers](#new-containers) - - [`DepositRequest`](#depositrequest) - - [`PendingDeposit`](#pendingdeposit) - - [`PendingPartialWithdrawal`](#pendingpartialwithdrawal) - - [`WithdrawalRequest`](#withdrawalrequest) - - [`ConsolidationRequest`](#consolidationrequest) - - [`PendingConsolidation`](#pendingconsolidation) - - [Modified Containers](#modified-containers) - - [`AttesterSlashing`](#attesterslashing) - - [Extended Containers](#extended-containers) - - [`Attestation`](#attestation) - - [`IndexedAttestation`](#indexedattestation) - - [`BeaconBlockBody`](#beaconblockbody) - - [`ExecutionPayload`](#executionpayload) - - [`ExecutionPayloadHeader`](#executionpayloadheader) - - [`BeaconState`](#beaconstate) -- [Helper functions](#helper-functions) - - [Predicates](#predicates) - - [Updated `compute_proposer_index`](#updated-compute_proposer_index) - - [Updated `is_eligible_for_activation_queue`](#updated-is_eligible_for_activation_queue) - - [New `is_compounding_withdrawal_credential`](#new-is_compounding_withdrawal_credential) - - [New `has_compounding_withdrawal_credential`](#new-has_compounding_withdrawal_credential) - - [New `has_execution_withdrawal_credential`](#new-has_execution_withdrawal_credential) - - [Updated `is_fully_withdrawable_validator`](#updated-is_fully_withdrawable_validator) - - [Updated `is_partially_withdrawable_validator`](#updated-is_partially_withdrawable_validator) - - [Misc](#misc-1) - - [`get_committee_indices`](#get_committee_indices) - - [`get_validator_max_effective_balance`](#get_validator_max_effective_balance) - - [Beacon state accessors](#beacon-state-accessors) - - [New `get_balance_churn_limit`](#new-get_balance_churn_limit) - - [New `get_activation_exit_churn_limit`](#new-get_activation_exit_churn_limit) - - [New `get_consolidation_churn_limit`](#new-get_consolidation_churn_limit) - - [New `get_active_balance`](#new-get_active_balance) - - [New `get_pending_balance_to_withdraw`](#new-get_pending_balance_to_withdraw) - - [Modified `get_attesting_indices`](#modified-get_attesting_indices) - - [New `get_activation_churn_consumption`](#new-get_activation_churn_consumption) - - [Modified `get_next_sync_committee_indices`](#modified-get_next_sync_committee_indices) - - [Beacon state mutators](#beacon-state-mutators) - - [Updated `initiate_validator_exit`](#updated--initiate_validator_exit) - - [New `switch_to_compounding_validator`](#new-switch_to_compounding_validator) - - [New `queue_excess_active_balance`](#new-queue_excess_active_balance) - - [New `queue_entire_balance_and_reset_validator`](#new-queue_entire_balance_and_reset_validator) - - [New `compute_exit_epoch_and_update_churn`](#new-compute_exit_epoch_and_update_churn) - - [New `compute_consolidation_epoch_and_update_churn`](#new-compute_consolidation_epoch_and_update_churn) - - [Updated `slash_validator`](#updated-slash_validator) -- [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Epoch processing](#epoch-processing) - - [Updated `process_epoch`](#updated-process_epoch) - - [Updated `process_registry_updates`](#updated--process_registry_updates) - - [New `apply_pending_deposit`](#new-apply_pending_deposit) - - [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) - - [Withdrawals](#withdrawals) - - [Updated `get_expected_withdrawals`](#updated-get_expected_withdrawals) - - [Updated `process_withdrawals`](#updated-process_withdrawals) - - [Execution payload](#execution-payload) - - [Modified `process_execution_payload`](#modified-process_execution_payload) - - [Operations](#operations) - - [Modified `process_operations`](#modified-process_operations) - - [Attestations](#attestations) - - [Modified `process_attestation`](#modified-process_attestation) - - [Deposits](#deposits) - - [Updated `apply_deposit`](#updated--apply_deposit) - - [New `is_valid_deposit_signature`](#new-is_valid_deposit_signature) - - [Voluntary exits](#voluntary-exits) - - [Updated `process_voluntary_exit`](#updated-process_voluntary_exit) - - [Execution layer withdrawal requests](#execution-layer-withdrawal-requests) - - [New `process_withdrawal_request`](#new-process_withdrawal_request) - - [Deposit requests](#deposit-requests) - - [New `process_deposit_request`](#new-process_deposit_request) - - [Execution layer consolidation requests](#execution-layer-consolidation-requests) - - [New `process_consolidation_request`](#new-process_consolidation_request) -- [Testing](#testing) +- [Electra -- The Beacon Chain](#electra----the-beacon-chain) + - [Table of contents](#table-of-contents) + - [Introduction](#introduction) + - [Constants](#constants) + - [Misc](#misc) + - [Withdrawal prefixes](#withdrawal-prefixes) + - [Domains](#domains) + - [Preset](#preset) + - [Gwei values](#gwei-values) + - [Rewards and penalties](#rewards-and-penalties) + - [State list lengths](#state-list-lengths) + - [Max operations per block](#max-operations-per-block) + - [Execution](#execution) + - [Withdrawals processing](#withdrawals-processing) + - [Pending deposits processing](#pending-deposits-processing) + - [Configuration](#configuration) + - [Validator cycle](#validator-cycle) + - [Containers](#containers) + - [New containers](#new-containers) + - [`DepositRequest`](#depositrequest) + - [`PendingDeposit`](#pendingdeposit) + - [`PendingPartialWithdrawal`](#pendingpartialwithdrawal) + - [`WithdrawalRequest`](#withdrawalrequest) + - [`ConsolidationRequest`](#consolidationrequest) + - [`PendingConsolidation`](#pendingconsolidation) + - [Modified Containers](#modified-containers) + - [`AttesterSlashing`](#attesterslashing) + - [Extended Containers](#extended-containers) + - [`Attestation`](#attestation) + - [`IndexedAttestation`](#indexedattestation) + - [`BeaconBlockBody`](#beaconblockbody) + - [`ExecutionPayload`](#executionpayload) + - [`ExecutionPayloadHeader`](#executionpayloadheader) + - [`BeaconState`](#beaconstate) + - [Helper functions](#helper-functions) + - [Predicates](#predicates) + - [Updated `compute_proposer_index`](#updated-compute_proposer_index) + - [Updated `is_eligible_for_activation_queue`](#updated-is_eligible_for_activation_queue) + - [New `is_compounding_withdrawal_credential`](#new-is_compounding_withdrawal_credential) + - [New `has_compounding_withdrawal_credential`](#new-has_compounding_withdrawal_credential) + - [New `has_execution_withdrawal_credential`](#new-has_execution_withdrawal_credential) + - [Updated `is_fully_withdrawable_validator`](#updated-is_fully_withdrawable_validator) + - [Updated `is_partially_withdrawable_validator`](#updated-is_partially_withdrawable_validator) + - [Misc](#misc-1) + - [`get_committee_indices`](#get_committee_indices) + - [`get_validator_max_effective_balance`](#get_validator_max_effective_balance) + - [Beacon state accessors](#beacon-state-accessors) + - [New `get_balance_churn_limit`](#new-get_balance_churn_limit) + - [New `get_activation_exit_churn_limit`](#new-get_activation_exit_churn_limit) + - [New `get_consolidation_churn_limit`](#new-get_consolidation_churn_limit) + - [New `get_active_balance`](#new-get_active_balance) + - [New `get_pending_balance_to_withdraw`](#new-get_pending_balance_to_withdraw) + - [Modified `get_attesting_indices`](#modified-get_attesting_indices) + - [New `get_activation_churn_consumption`](#new-get_activation_churn_consumption) + - [Modified `get_next_sync_committee_indices`](#modified-get_next_sync_committee_indices) + - [Beacon state mutators](#beacon-state-mutators) + - [Updated `initiate_validator_exit`](#updated--initiate_validator_exit) + - [New `switch_to_compounding_validator`](#new-switch_to_compounding_validator) + - [New `queue_excess_active_balance`](#new-queue_excess_active_balance) + - [New `queue_entire_balance_and_reset_validator`](#new-queue_entire_balance_and_reset_validator) + - [New `compute_exit_epoch_and_update_churn`](#new-compute_exit_epoch_and_update_churn) + - [New `compute_consolidation_epoch_and_update_churn`](#new-compute_consolidation_epoch_and_update_churn) + - [Updated `slash_validator`](#updated-slash_validator) + - [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Epoch processing](#epoch-processing) + - [Updated `process_epoch`](#updated-process_epoch) + - [Updated `process_registry_updates`](#updated--process_registry_updates) + - [New `apply_pending_deposit`](#new-apply_pending_deposit) + - [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) + - [Withdrawals](#withdrawals) + - [Updated `get_expected_withdrawals`](#updated-get_expected_withdrawals) + - [Updated `process_withdrawals`](#updated-process_withdrawals) + - [Execution payload](#execution-payload) + - [Modified `process_execution_payload`](#modified-process_execution_payload) + - [Operations](#operations) + - [Modified `process_operations`](#modified-process_operations) + - [Attestations](#attestations) + - [Modified `process_attestation`](#modified-process_attestation) + - [Deposits](#deposits) + - [Updated `apply_deposit`](#updated--apply_deposit) + - [New `is_valid_deposit_signature`](#new-is_valid_deposit_signature) + - [Voluntary exits](#voluntary-exits) + - [Updated `process_voluntary_exit`](#updated-process_voluntary_exit) + - [Execution layer withdrawal requests](#execution-layer-withdrawal-requests) + - [New `process_withdrawal_request`](#new-process_withdrawal_request) + - [Deposit requests](#deposit-requests) + - [New `process_deposit_request`](#new-process_deposit_request) + - [Execution layer consolidation requests](#execution-layer-consolidation-requests) + - [New `process_consolidation_request`](#new-process_consolidation_request) + - [Testing](#testing) @@ -954,7 +956,7 @@ def process_pending_deposits(state: BeaconState) -> None: break # Check if number of processed deposits has not reached the limit, otherwise, stop processing. - if next_deposit_index > MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: + if next_deposit_index > MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING-1: break # Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 1318f7f6f2..25352a05a0 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -279,3 +279,42 @@ def test_processing_deposit_of_withdrawable_validator_does_not_get_churned(spec, # First deposit does not consume any. assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(state) assert state.pending_deposits == [build_pending_deposit_top_up(spec, state, validator_index=1, amount=amount)] + +@with_electra_and_later +@spec_state_test +def test_pending_deposit_over_max(spec, state): + # pick an amount that adds to less than churn limit + amount = 100 + overmax = spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING + 1 + for i in range(overmax): + state.pending_deposits.append(spec.PendingDeposit( + pubkey=state.validators[i].pubkey, + withdrawal_credentials=state.validators[i].withdrawal_credentials, + amount=amount, + slot=state.slot + )) + + assert len(state.pending_deposits) == overmax,"pending deposits is not over max" + yield from run_process_pending_deposits(spec, state) + # the remaining deposit over MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING should remain in pending_deposits + assert len(state.pending_deposits) == 1 + +@with_electra_and_later +@spec_state_test +def test_pending_deposit_deposit_not_finalized(spec, state): + amount = spec.MIN_ACTIVATION_BALANCE + slot=spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) + # deposit is not finalized yet, so it is postponed + state.pending_deposits.append(spec.PendingDeposit( + pubkey=state.validators[0].pubkey, + withdrawal_credentials=state.validators[0].withdrawal_credentials, + amount=amount, + slot=slot, + )) + # set deposit_balance_to_consume to some initial amount to see its removal later on in the test + state.deposit_balance_to_consume = amount + yield from run_process_pending_deposits(spec, state) + # deposit_balance_to_consume was reset to 0 + assert state.deposit_balance_to_consume == 0 + # deposit was postponed and not processed + assert len(state.pending_deposits) == 1 From 5c0a8d3b71a7e1eaffbe440f1bc8abb0c3bc9cc0 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 2 Jul 2024 15:30:42 +0600 Subject: [PATCH 008/103] Fix off-by-one found by @james-prysm --- specs/electra/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 83dd728a77..d998a9254a 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -954,7 +954,7 @@ def process_pending_deposits(state: BeaconState) -> None: break # Check if number of processed deposits has not reached the limit, otherwise, stop processing. - if next_deposit_index > MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: + if next_deposit_index >= MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: break # Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. From 2af84d833292e314e79107eebf516ee6c008878f Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Tue, 2 Jul 2024 08:58:30 -0500 Subject: [PATCH 009/103] Update tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py Co-authored-by: Mikhail Kalinin --- .../electra/epoch_processing/test_process_pending_deposits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 25352a05a0..d8f211fd4a 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -291,7 +291,7 @@ def test_pending_deposit_over_max(spec, state): pubkey=state.validators[i].pubkey, withdrawal_credentials=state.validators[i].withdrawal_credentials, amount=amount, - slot=state.slot + slot=spec.GENESIS_SLOT )) assert len(state.pending_deposits) == overmax,"pending deposits is not over max" From 750f7a1bd6878e5842e96aec4dd47920dec4908f Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 2 Jul 2024 12:02:40 -0500 Subject: [PATCH 010/103] adding new test --- .../test_process_pending_deposits.py | 103 +++++++++++------- 1 file changed, 65 insertions(+), 38 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 25352a05a0..33eeca52cc 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -11,6 +11,71 @@ def run_process_pending_deposits(spec, state): yield from run_epoch_processing_with(spec, state, 'process_pending_deposits') +@with_electra_and_later +@spec_state_test +def test_pending_deposit_over_max(spec, state): + # pick an amount that adds to less than churn limit + amount = 100 + overmax = spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING + 1 + for i in range(overmax): + state.pending_deposits.append(spec.PendingDeposit( + pubkey=state.validators[i].pubkey, + withdrawal_credentials=state.validators[i].withdrawal_credentials, + amount=amount, + slot=spec.GENESIS_SLOT, + )) + assert len(state.pending_deposits) == overmax,"pending deposits is not over max" + yield from run_process_pending_deposits(spec, state) + # the remaining deposit over MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING should remain in pending_deposits + assert len(state.pending_deposits) == 1 + +@with_electra_and_later +@spec_state_test +def test_pending_deposit_eth1_bridge_not_applied(spec, state): + amount = spec.MIN_ACTIVATION_BALANCE + # deposit is not finalized yet, so it is postponed + state.pending_deposits.append(spec.PendingDeposit( + pubkey=state.validators[0].pubkey, + withdrawal_credentials=state.validators[0].withdrawal_credentials, + amount=amount, + slot=1, + )) + # set deposit_requests_start_index to something high so that deposit is not processed + state.deposit_requests_start_index = 100000000000000000 + # set deposit_balance_to_consume to some initial amount to see its removal later on in the test + state.deposit_balance_to_consume = amount + yield from run_process_pending_deposits(spec, state) + # deposit_balance_to_consume was reset to 0 + assert state.deposit_balance_to_consume == 0 + # deposit was postponed and not processed + assert len(state.pending_deposits) == 1 + + +@with_electra_and_later +@spec_state_test +def test_pending_deposit_deposit_not_finalized(spec, state): + amount = spec.MIN_ACTIVATION_BALANCE + # set slot to something not finalized + slot=spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch+1) + # deposit is not finalized yet, so it is postponed + state.pending_deposits.append(spec.PendingDeposit( + pubkey=state.validators[0].pubkey, + withdrawal_credentials=state.validators[0].withdrawal_credentials, + amount=amount, + slot=slot, + )) + # set deposit_requests_start_index to something low so that we skip the bridge validation + state.deposit_requests_start_index = 0 + print("deposit indexes",state.eth1_deposit_index,state.deposit_requests_start_index) + # set deposit_balance_to_consume to some initial amount to see its removal later on in the test + state.deposit_balance_to_consume = amount + yield from run_process_pending_deposits(spec, state) + # deposit_balance_to_consume was reset to 0 + assert state.deposit_balance_to_consume == 0 + # deposit was postponed and not processed + assert len(state.pending_deposits) == 1 + + @with_electra_and_later @spec_state_test @@ -280,41 +345,3 @@ def test_processing_deposit_of_withdrawable_validator_does_not_get_churned(spec, assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(state) assert state.pending_deposits == [build_pending_deposit_top_up(spec, state, validator_index=1, amount=amount)] -@with_electra_and_later -@spec_state_test -def test_pending_deposit_over_max(spec, state): - # pick an amount that adds to less than churn limit - amount = 100 - overmax = spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING + 1 - for i in range(overmax): - state.pending_deposits.append(spec.PendingDeposit( - pubkey=state.validators[i].pubkey, - withdrawal_credentials=state.validators[i].withdrawal_credentials, - amount=amount, - slot=state.slot - )) - - assert len(state.pending_deposits) == overmax,"pending deposits is not over max" - yield from run_process_pending_deposits(spec, state) - # the remaining deposit over MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING should remain in pending_deposits - assert len(state.pending_deposits) == 1 - -@with_electra_and_later -@spec_state_test -def test_pending_deposit_deposit_not_finalized(spec, state): - amount = spec.MIN_ACTIVATION_BALANCE - slot=spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) - # deposit is not finalized yet, so it is postponed - state.pending_deposits.append(spec.PendingDeposit( - pubkey=state.validators[0].pubkey, - withdrawal_credentials=state.validators[0].withdrawal_credentials, - amount=amount, - slot=slot, - )) - # set deposit_balance_to_consume to some initial amount to see its removal later on in the test - state.deposit_balance_to_consume = amount - yield from run_process_pending_deposits(spec, state) - # deposit_balance_to_consume was reset to 0 - assert state.deposit_balance_to_consume == 0 - # deposit was postponed and not processed - assert len(state.pending_deposits) == 1 From b31fa63a4a07f68327a6eebc205ac2ea098b2738 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 2 Jul 2024 15:50:01 -0500 Subject: [PATCH 011/103] apply pending deposit tests --- .../test_apply_pending_deposit.py | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py new file mode 100644 index 0000000000..a521079bbf --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -0,0 +1,116 @@ +from eth2spec.test.context import ( + spec_state_test, + with_electra_and_later, +) +from eth2spec.test.helpers.keys import privkeys, pubkeys +from tests.core.pyspec.eth2spec.test.helpers.deposits import build_deposit_data + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_add_validator_to_registry(spec, state): + amount = 100 + + # choose a value public key that's not in the validator set outside of the mainnet preset of 256 + index = 2000 + 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) + assert spec.apply_pending_deposit(state,deposit) == True + # validator count should increase by 1 + assert len(state.validators) == old_validator_count+1 + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_not_withdrawable_epoch_while_exiting(spec, state): + amount = 100 + # validator exit epoch must be less than far future + state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH - 1 + state.validators[0].withdrawable_epoch = spec.FAR_FUTURE_EPOCH + + deposit = spec.PendingDeposit( + pubkey=state.validators[0].pubkey, + withdrawal_credentials= state.validators[0].withdrawal_credentials, + amount=amount, + slot=spec.GENESIS_SLOT, + ) + assert spec.apply_pending_deposit(state,deposit) == False + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_withdrawable_epoch_while_exiting(spec, state): + amount = 100 + state.slot = spec.SLOTS_PER_EPOCH * 2 + # validator exit epoch must be less than far future + state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH - 1 + state.validators[0].withdrawable_epoch = 0 + + deposit = spec.PendingDeposit( + pubkey=state.validators[0].pubkey, + withdrawal_credentials= state.validators[0].withdrawal_credentials, + amount=amount, + slot=spec.GENESIS_SLOT, + ) + # reset the balance + state.balances[0] = 0 + assert spec.apply_pending_deposit(state,deposit) == True + assert state.balances[0] == amount + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_withdrawable_epoch_while_exited(spec, state): + amount = 100 + state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH + # 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, + ) + # reset the balance + state.balances[0] = 0 + assert spec.apply_pending_deposit(state,deposit) == True + assert state.balances[0] == amount + + +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_switch_to_compounding(spec, state): + amount = 100 + + # choose a value public key that's not 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 + state.validators[index].exit_epoch = spec.FAR_FUTURE_EPOCH + 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[0] = 0 + assert spec.apply_pending_deposit(state,deposit) == True + # validator count should increase by 1 + assert state.balances[0] == amount + assert state.validators[0].withdrawal_credentials == compounding_credentials \ No newline at end of file From b2cd3ce9a7058d233f891af9f9e1e061e6c1cb72 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 2 Jul 2024 15:51:29 -0500 Subject: [PATCH 012/103] fixing comment --- .../electra/epoch_processing/test_apply_pending_deposit.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index a521079bbf..548116eba9 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -90,7 +90,7 @@ def test_apply_pending_deposit_withdrawable_epoch_while_exited(spec, state): def test_apply_pending_deposit_switch_to_compounding(spec, state): amount = 100 - # choose a value public key that's not in the validator set + # 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:] @@ -113,4 +113,5 @@ def test_apply_pending_deposit_switch_to_compounding(spec, state): assert spec.apply_pending_deposit(state,deposit) == True # validator count should increase by 1 assert state.balances[0] == amount - assert state.validators[0].withdrawal_credentials == compounding_credentials \ No newline at end of file + assert state.validators[0].withdrawal_credentials == compounding_credentials + \ No newline at end of file From 03071df6e39ba764ac94dcc44a0fbe74e219bd1f Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 2 Jul 2024 22:10:15 -0500 Subject: [PATCH 013/103] preston's feedback --- .../test_apply_pending_deposit.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index 548116eba9..ef40a030fd 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -47,6 +47,22 @@ def test_apply_pending_deposit_not_withdrawable_epoch_while_exiting(spec, state) ) assert spec.apply_pending_deposit(state,deposit) == False +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_not_withdrawable_epoch_at_current_epoch_while_exiting(spec, state): + amount = 100 + # validator exit epoch must be less than far future + state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH - 1 + state.validators[0].withdrawable_epoch = spec.get_current_epoch(state) + + deposit = spec.PendingDeposit( + pubkey=state.validators[0].pubkey, + withdrawal_credentials= state.validators[0].withdrawal_credentials, + amount=amount, + slot=spec.GENESIS_SLOT, + ) + assert spec.apply_pending_deposit(state,deposit) == False + @with_electra_and_later @spec_state_test def test_apply_pending_deposit_withdrawable_epoch_while_exiting(spec, state): @@ -67,6 +83,7 @@ def test_apply_pending_deposit_withdrawable_epoch_while_exiting(spec, state): assert spec.apply_pending_deposit(state,deposit) == True assert state.balances[0] == amount + @with_electra_and_later @spec_state_test def test_apply_pending_deposit_withdrawable_epoch_while_exited(spec, state): From ee4da7b58a049c72b7e8131b64743e76b0878993 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 5 Jul 2024 18:04:38 +0600 Subject: [PATCH 014/103] Refactor postponed deposit processing --- specs/electra/beacon-chain.md | 99 ++++++++++++++--------------------- 1 file changed, 38 insertions(+), 61 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index ec4cbac2ac..bce8ed2a39 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -59,7 +59,6 @@ - [New `get_active_balance`](#new-get_active_balance) - [New `get_pending_balance_to_withdraw`](#new-get_pending_balance_to_withdraw) - [Modified `get_attesting_indices`](#modified-get_attesting_indices) - - [New `get_activation_churn_consumption`](#new-get_activation_churn_consumption) - [Modified `get_next_sync_committee_indices`](#modified-get_next_sync_committee_indices) - [Beacon state mutators](#beacon-state-mutators) - [Modified `initiate_validator_exit`](#modified-initiate_validator_exit) @@ -633,27 +632,6 @@ def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[V return output ``` -#### New `get_activation_churn_consumption` - -```python -def get_activation_churn_consumption(state: BeaconState, deposit: PendingDeposit) -> Gwei: - """ - Return amount of activation churn consumed by the ``deposit``. - """ - validator_pubkeys = [v.pubkey for v in state.validators] - - if deposit.pubkey not in validator_pubkeys: - return deposit.amount - else: - validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey)) - validator = state.validators[validator_index] - # Validator is exiting, do not consume the churn - if validator.exit_epoch < FAR_FUTURE_EPOCH: - return Gwei(0) - else: - return deposit.amount -``` - #### Modified `get_next_sync_committee_indices` *Note*: The function `get_next_sync_committee_indices` is modified to use `MAX_EFFECTIVE_BALANCE_ELECTRA`. @@ -892,11 +870,9 @@ def process_registry_updates(state: BeaconState) -> None: #### New `apply_pending_deposit` ```python -def apply_pending_deposit(state: BeaconState, deposit: PendingDeposit) -> bool: +def apply_pending_deposit(state: BeaconState, deposit: PendingDeposit) -> None: """ Applies ``deposit`` to the ``state``. - Returns ``True`` if ``deposit`` has been successfully applied - and can be removed from the queue. """ validator_pubkeys = [v.pubkey for v in state.validators] if deposit.pubkey not in validator_pubkeys: @@ -910,32 +886,22 @@ def apply_pending_deposit(state: BeaconState, deposit: PendingDeposit) -> bool: add_validator_to_registry(state, deposit.pubkey, deposit.withdrawal_credentials, deposit.amount) else: validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey)) + # Increase balance + increase_balance(state, validator_index, deposit.amount) + # If validator has not exited, check if valid deposit switch to compounding credentials. 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: - return False - # 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: - # Increase balance - increase_balance(state, validator_index, 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) - - return True + if ( + validator.exit_epoch < get_current_epoch(state) + and 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) ``` #### New `process_pending_deposits` @@ -973,19 +939,30 @@ def process_pending_deposits(state: BeaconState) -> None: if next_deposit_index >= MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: break - # Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. - churn_consumption = get_activation_churn_consumption(state, deposit) - if processed_amount + churn_consumption > available_for_processing: - is_churn_limit_reached = True - break - - # Consume churn and apply deposit - processed_amount += churn_consumption - is_deposit_applied = apply_pending_deposit(state, deposit) + # Read validator state + is_validator_exited = False + is_validator_withdrawn = False + validator_pubkeys = [v.pubkey for v in state.validators] + if deposit.pubkey: + validator = state.validators[ValidatorIndex(validator_pubkeys.index(deposit.pubkey))] + is_validator_exited = validator.exit_epoch < FAR_FUTURE_EPOCH + is_validator_withdrawn = validator.withdrawable_epoch < get_current_epoch(state) - # Postpone deposit if it has not been applied. - if not is_deposit_applied: + if is_validator_withdrawn: + # Deposited balance will never become active. Increase balance but do not consume churn + apply_pending_deposit(state, deposit) + elif is_validator_exited: + # Validator is exiting, postpone the deposit until after withdrawable epoch deposits_to_postpone.append(deposit) + else: + # Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. + is_churn_limit_reached = processed_amount + deposit.amount > available_for_processing + if is_churn_limit_reached: + break + + # Consume churn and apply deposit. + processed_amount += deposit.amount + apply_pending_deposit(state, deposit) # Regardless of how the deposit was handled, we move on in the queue. next_deposit_index += 1 From 6ed15b2818908a17070d1ffb35f520e605f64b66 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 5 Jul 2024 18:49:57 +0600 Subject: [PATCH 015/103] Fix electra fork tests --- .../eth2spec/test/electra/fork/test_electra_fork_basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/fork/test_electra_fork_basic.py b/tests/core/pyspec/eth2spec/test/electra/fork/test_electra_fork_basic.py index 852521a32b..e569be35e3 100644 --- a/tests/core/pyspec/eth2spec/test/electra/fork/test_electra_fork_basic.py +++ b/tests/core/pyspec/eth2spec/test/electra/fork/test_electra_fork_basic.py @@ -91,7 +91,7 @@ def test_fork_pre_activation(spec, phases, state): state.validators[0].activation_epoch = spec.FAR_FUTURE_EPOCH post_state = yield from run_fork_test(post_spec, state) - assert len(post_state.pending_balance_deposits) > 0 + assert len(post_state.pending_deposits) > 0 @with_phases(phases=[DENEB], other_phases=[ELECTRA]) @@ -105,4 +105,4 @@ def test_fork_has_compounding_withdrawal_credential(spec, phases, state): validator.withdrawal_credentials = post_spec.COMPOUNDING_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] post_state = yield from run_fork_test(post_spec, state) - assert len(post_state.pending_balance_deposits) > 0 + assert len(post_state.pending_deposits) > 0 From 1a19d82bbcc74c59a55336a5a03a7e1f3bf48765 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 8 Jul 2024 23:15:03 -0500 Subject: [PATCH 016/103] updating tests based on mikhail changes --- .../test_apply_pending_deposit.py | 86 ++++++++----------- .../test_process_pending_deposits.py | 61 ++++++++++++- 2 files changed, 95 insertions(+), 52 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index ef40a030fd..e564284c58 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -33,45 +33,10 @@ def test_apply_pending_deposit_add_validator_to_registry(spec, state): @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_not_withdrawable_epoch_while_exiting(spec, state): +def test_apply_pending_deposit_increases_balance(spec, state): amount = 100 - # validator exit epoch must be less than far future - state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH - 1 - state.validators[0].withdrawable_epoch = spec.FAR_FUTURE_EPOCH - - deposit = spec.PendingDeposit( - pubkey=state.validators[0].pubkey, - withdrawal_credentials= state.validators[0].withdrawal_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - ) - assert spec.apply_pending_deposit(state,deposit) == False - -@with_electra_and_later -@spec_state_test -def test_apply_pending_deposit_not_withdrawable_epoch_at_current_epoch_while_exiting(spec, state): - amount = 100 - # validator exit epoch must be less than far future - state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH - 1 - state.validators[0].withdrawable_epoch = spec.get_current_epoch(state) - - deposit = spec.PendingDeposit( - pubkey=state.validators[0].pubkey, - withdrawal_credentials= state.validators[0].withdrawal_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - ) - assert spec.apply_pending_deposit(state,deposit) == False - -@with_electra_and_later -@spec_state_test -def test_apply_pending_deposit_withdrawable_epoch_while_exiting(spec, state): - amount = 100 - state.slot = spec.SLOTS_PER_EPOCH * 2 - # validator exit epoch must be less than far future - state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH - 1 - state.validators[0].withdrawable_epoch = 0 - + state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH + # 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, @@ -80,31 +45,47 @@ def test_apply_pending_deposit_withdrawable_epoch_while_exiting(spec, state): ) # reset the balance state.balances[0] = 0 - assert spec.apply_pending_deposit(state,deposit) == True + # run test + spec.apply_pending_deposit(state,deposit) assert state.balances[0] == amount @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_withdrawable_epoch_while_exited(spec, state): +def test_apply_pending_deposit_switch_to_compounding(spec, state): amount = 100 - state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH - # signature doesn't matter here as it's interpreted as a top-up + + # 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.slot = spec.SLOTS_PER_EPOCH * 2 + 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=state.validators[0].pubkey, - withdrawal_credentials= state.validators[0].withdrawal_credentials, + pubkey=pubkeys[index], + withdrawal_credentials= compounding_credentials, amount=amount, slot=spec.GENESIS_SLOT, + signature=deposit_data.signature, ) - # reset the balance state.balances[0] = 0 - assert spec.apply_pending_deposit(state,deposit) == True + # run test + spec.apply_pending_deposit(state,deposit) + # validator balance should increase assert state.balances[0] == amount - + assert state.validators[0].withdrawal_credentials == compounding_credentials @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_switch_to_compounding(spec, state): +def test_apply_pending_deposit_switch_to_compounding_while_validator_not_exited(spec, state): amount = 100 # choose a value public key that's in the validator set @@ -112,6 +93,7 @@ def test_apply_pending_deposit_switch_to_compounding(spec, state): 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 + # set validator to not be exited state.validators[index].exit_epoch = spec.FAR_FUTURE_EPOCH deposit_data = build_deposit_data(spec, pubkeys[index], @@ -127,8 +109,10 @@ def test_apply_pending_deposit_switch_to_compounding(spec, state): signature=deposit_data.signature, ) state.balances[0] = 0 - assert spec.apply_pending_deposit(state,deposit) == True - # validator count should increase by 1 + # run test + spec.apply_pending_deposit(state,deposit) + # validator balance should increase assert state.balances[0] == amount - assert state.validators[0].withdrawal_credentials == compounding_credentials + # make sure validator did not switch to compounding if not exited + assert state.validators[0].withdrawal_credentials == withdrawal_credentials \ No newline at end of file diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index ecd33d0bb3..6d27d23311 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -53,7 +53,7 @@ def test_pending_deposit_eth1_bridge_not_applied(spec, state): @with_electra_and_later @spec_state_test -def test_pending_deposit_deposit_not_finalized(spec, state): +def test_pending_deposit_not_finalized(spec, state): amount = spec.MIN_ACTIVATION_BALANCE # set slot to something not finalized slot=spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch+1) @@ -75,6 +75,65 @@ def test_pending_deposit_deposit_not_finalized(spec, state): # deposit was postponed and not processed assert len(state.pending_deposits) == 1 +@with_electra_and_later +@spec_state_test +def test_pending_deposit_validator_withdrawn(spec, state): + amount = spec.MIN_ACTIVATION_BALANCE + + withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + spec.hash(state.validators[0].pubkey)[1:] + state.slot = spec.SLOTS_PER_EPOCH * 2 + state.validators[0].withdrawal_credentials = withdrawal_credentials + # set validator to be withdrawable by current epoch + state.validators[0].withdrawable_epoch = spec.get_current_epoch(state) - 1 + state.pending_deposits.append(spec.PendingDeposit( + pubkey=state.validators[0].pubkey, + withdrawal_credentials=state.validators[0].withdrawal_credentials, + amount=amount, + slot=spec.GENESIS_SLOT, + )) + # set deposit_requests_start_index to something low so that we skip the bridge validation + state.deposit_requests_start_index = 0 + print("deposit indexes",state.eth1_deposit_index,state.deposit_requests_start_index) + # set deposit_balance_to_consume to some initial amount to see its removal later on in the test + state.deposit_balance_to_consume = amount + # reset balance for assert + state.balances[0] = 0 + yield from run_process_pending_deposits(spec, state) + # deposit_balance_to_consume was reset to 0 + assert state.deposit_balance_to_consume == 0 + # deposit was processed + assert state.pending_deposits == [] + # balance increases because of withdraw + assert state.balances[0] == amount + +@with_electra_and_later +@spec_state_test +def test_pending_deposit_validator_exiting_but_not_withdrawn(spec, state): + amount = spec.MIN_ACTIVATION_BALANCE + + withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + spec.hash(state.validators[0].pubkey)[1:] + state.slot = spec.SLOTS_PER_EPOCH * 2 + state.validators[0].withdrawal_credentials = withdrawal_credentials + # set validator to be withdrawable by current epoch + state.validators[0].exit_epoch = spec.get_current_epoch(state) - 1 + state.validators[0].withdrawable_epoch = spec.FAR_FUTURE_EPOCH + state.pending_deposits.append(spec.PendingDeposit( + pubkey=state.validators[0].pubkey, + withdrawal_credentials=state.validators[0].withdrawal_credentials, + amount=amount, + slot=spec.GENESIS_SLOT, + )) + # set deposit_requests_start_index to something low so that we skip the bridge validation + state.deposit_requests_start_index = 0 + print("deposit indexes",state.eth1_deposit_index,state.deposit_requests_start_index) + # set deposit_balance_to_consume to some initial amount to see its removal later on in the test + state.deposit_balance_to_consume = amount + + yield from run_process_pending_deposits(spec, state) + # deposit_balance_to_consume was reset to 0 + assert state.deposit_balance_to_consume == 0 + # deposit was postponed and not processed + assert len(state.pending_deposits) == 1 @with_electra_and_later From 85b752be5e43a3b462722bd37bbaadc692719f92 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 9 Jul 2024 18:52:23 +0600 Subject: [PATCH 017/103] Fix by @pawanjay176 Co-authored-by: Pawan Dhananjay --- specs/electra/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index bce8ed2a39..fb11c463ac 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -943,7 +943,7 @@ def process_pending_deposits(state: BeaconState) -> None: is_validator_exited = False is_validator_withdrawn = False validator_pubkeys = [v.pubkey for v in state.validators] - if deposit.pubkey: + if deposit.pubkey in validator_pubkeys: validator = state.validators[ValidatorIndex(validator_pubkeys.index(deposit.pubkey))] is_validator_exited = validator.exit_epoch < FAR_FUTURE_EPOCH is_validator_withdrawn = validator.withdrawable_epoch < get_current_epoch(state) From 1e9337ef194d26faf592fa2582349f729beba3c2 Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:42:10 -0500 Subject: [PATCH 018/103] Update tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py Co-authored-by: Mikhail Kalinin --- .../test/electra/epoch_processing/test_apply_pending_deposit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index e564284c58..019db4f873 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -27,7 +27,7 @@ def test_apply_pending_deposit_add_validator_to_registry(spec, state): signature=deposit_data.signature, ) old_validator_count = len(state.validators) - assert spec.apply_pending_deposit(state,deposit) == True + spec.apply_pending_deposit(state,deposit) # validator count should increase by 1 assert len(state.validators) == old_validator_count+1 From 577b0289c07a0d733edf55dd301756b1cd491b2f Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:44:47 -0500 Subject: [PATCH 019/103] Update tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py Co-authored-by: Mikhail Kalinin --- .../electra/epoch_processing/test_process_pending_deposits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 6d27d23311..e6319aa6ba 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -62,7 +62,7 @@ def test_pending_deposit_not_finalized(spec, state): pubkey=state.validators[0].pubkey, withdrawal_credentials=state.validators[0].withdrawal_credentials, amount=amount, - slot=slot, + slot=spec.get_current_epoch(state), )) # set deposit_requests_start_index to something low so that we skip the bridge validation state.deposit_requests_start_index = 0 From 8b191e679d017c9a616a0a680e1f4ccb298b595c Mon Sep 17 00:00:00 2001 From: james-prysm Date: Wed, 10 Jul 2024 11:41:18 -0500 Subject: [PATCH 020/103] fixing linting --- .../test_apply_pending_deposit.py | 101 ++++++----- .../test_process_pending_deposits.py | 170 +++++++++++------- 2 files changed, 167 insertions(+), 104 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index 019db4f873..c5ed4aa3a7 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -5,114 +5,129 @@ from eth2spec.test.helpers.keys import privkeys, pubkeys from tests.core.pyspec.eth2spec.test.helpers.deposits import build_deposit_data + @with_electra_and_later @spec_state_test def test_apply_pending_deposit_add_validator_to_registry(spec, state): - amount = 100 + amount = 100 - # choose a value public key that's not in the validator set outside of the mainnet preset of 256 + # select validator set outside of the mainnet preset of 256 index = 2000 - withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkeys[index])[1:] + 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) + pubkeys[index], + privkeys[index], + amount, + withdrawal_credentials, + signed=True) deposit = spec.PendingDeposit( pubkey=pubkeys[index], - withdrawal_credentials= withdrawal_credentials, + withdrawal_credentials=withdrawal_credentials, amount=amount, slot=spec.GENESIS_SLOT, signature=deposit_data.signature, - ) + ) old_validator_count = len(state.validators) - spec.apply_pending_deposit(state,deposit) + spec.apply_pending_deposit(state, deposit) # validator count should increase by 1 - assert len(state.validators) == old_validator_count+1 + assert len(state.validators) == old_validator_count + 1 + @with_electra_and_later @spec_state_test def test_apply_pending_deposit_increases_balance(spec, state): - amount = 100 + amount = 100 state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH # 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, + withdrawal_credentials=state.validators[0].withdrawal_credentials, amount=amount, - slot=spec.GENESIS_SLOT, - ) - # reset the balance + slot=spec.GENESIS_SLOT + ) + # reset the balance state.balances[0] = 0 # run test - spec.apply_pending_deposit(state,deposit) + spec.apply_pending_deposit(state, deposit) assert state.balances[0] == amount @with_electra_and_later @spec_state_test def test_apply_pending_deposit_switch_to_compounding(spec, state): - amount = 100 + 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:] + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + spec.hash(pubkeys[index])[1:] + ) + compounding_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + spec.hash(pubkeys[index])[1:] + ) state.slot = spec.SLOTS_PER_EPOCH * 2 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) + pubkeys[index], + privkeys[index], + amount, + compounding_credentials, + signed=True) deposit = spec.PendingDeposit( pubkey=pubkeys[index], - withdrawal_credentials= compounding_credentials, + withdrawal_credentials=compounding_credentials, amount=amount, slot=spec.GENESIS_SLOT, signature=deposit_data.signature, - ) + ) state.balances[0] = 0 # run test - spec.apply_pending_deposit(state,deposit) + spec.apply_pending_deposit(state, deposit) # validator balance should increase assert state.balances[0] == amount - assert state.validators[0].withdrawal_credentials == compounding_credentials + 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_while_validator_not_exited(spec, state): - amount = 100 +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:] + 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 # set validator to not be exited state.validators[index].exit_epoch = spec.FAR_FUTURE_EPOCH deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - compounding_credentials, - signed=True) + pubkeys[index], + privkeys[index], + amount, + compounding_credentials, + signed=True) deposit = spec.PendingDeposit( pubkey=pubkeys[index], - withdrawal_credentials= compounding_credentials, + withdrawal_credentials=compounding_credentials, amount=amount, slot=spec.GENESIS_SLOT, signature=deposit_data.signature, - ) + ) state.balances[0] = 0 # run test - spec.apply_pending_deposit(state,deposit) + spec.apply_pending_deposit(state, deposit) # validator balance should increase assert state.balances[0] == amount # make sure validator did not switch to compounding if not exited assert state.validators[0].withdrawal_credentials == withdrawal_credentials - \ No newline at end of file diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index e6319aa6ba..207bf89400 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -9,26 +9,35 @@ def run_process_pending_deposits(spec, state): - yield from run_epoch_processing_with(spec, state, 'process_pending_deposits') + yield from run_epoch_processing_with( + spec, state, 'process_pending_deposits') + @with_electra_and_later @spec_state_test def test_pending_deposit_over_max(spec, state): # pick an amount that adds to less than churn limit - amount = 100 + amount = 100 overmax = spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING + 1 for i in range(overmax): - state.pending_deposits.append(spec.PendingDeposit( - pubkey=state.validators[i].pubkey, - withdrawal_credentials=state.validators[i].withdrawal_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - )) - assert len(state.pending_deposits) == overmax,"pending deposits is not over max" + state.pending_deposits.append( + spec.PendingDeposit( + pubkey=state.validators[i].pubkey, + withdrawal_credentials=( + state.validators[i].withdrawal_credentials + ), + amount=amount, + slot=spec.GENESIS_SLOT, + ) + ) + assert len(state.pending_deposits) == overmax, \ + "pending deposits is not overmax" + # the remaining deposit over MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING + # should remain in pending_deposits yield from run_process_pending_deposits(spec, state) - # the remaining deposit over MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING should remain in pending_deposits assert len(state.pending_deposits) == 1 + @with_electra_and_later @spec_state_test def test_pending_deposit_eth1_bridge_not_applied(spec, state): @@ -40,9 +49,9 @@ def test_pending_deposit_eth1_bridge_not_applied(spec, state): amount=amount, slot=1, )) - # set deposit_requests_start_index to something high so that deposit is not processed + # set deposit_requests_start_index to something high state.deposit_requests_start_index = 100000000000000000 - # set deposit_balance_to_consume to some initial amount to see its removal later on in the test + # set deposit_balance_to_consume to some initial amount state.deposit_balance_to_consume = amount yield from run_process_pending_deposits(spec, state) # deposit_balance_to_consume was reset to 0 @@ -56,18 +65,16 @@ def test_pending_deposit_eth1_bridge_not_applied(spec, state): def test_pending_deposit_not_finalized(spec, state): amount = spec.MIN_ACTIVATION_BALANCE # set slot to something not finalized - slot=spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch+1) # deposit is not finalized yet, so it is postponed state.pending_deposits.append(spec.PendingDeposit( pubkey=state.validators[0].pubkey, withdrawal_credentials=state.validators[0].withdrawal_credentials, amount=amount, - slot=spec.get_current_epoch(state), + slot=spec.get_current_epoch(state) + 1, )) - # set deposit_requests_start_index to something low so that we skip the bridge validation + # skip the bridge validation state.deposit_requests_start_index = 0 - print("deposit indexes",state.eth1_deposit_index,state.deposit_requests_start_index) - # set deposit_balance_to_consume to some initial amount to see its removal later on in the test + # set deposit_balance_to_consume to some initial amount state.deposit_balance_to_consume = amount yield from run_process_pending_deposits(spec, state) # deposit_balance_to_consume was reset to 0 @@ -75,12 +82,13 @@ def test_pending_deposit_not_finalized(spec, state): # deposit was postponed and not processed assert len(state.pending_deposits) == 1 + @with_electra_and_later @spec_state_test def test_pending_deposit_validator_withdrawn(spec, state): amount = spec.MIN_ACTIVATION_BALANCE - - withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + spec.hash(state.validators[0].pubkey)[1:] + hash = spec.hash(state.validators[0].pubkey)[1:] + withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + hash state.slot = spec.SLOTS_PER_EPOCH * 2 state.validators[0].withdrawal_credentials = withdrawal_credentials # set validator to be withdrawable by current epoch @@ -91,10 +99,9 @@ def test_pending_deposit_validator_withdrawn(spec, state): amount=amount, slot=spec.GENESIS_SLOT, )) - # set deposit_requests_start_index to something low so that we skip the bridge validation + # skip the bridge validation state.deposit_requests_start_index = 0 - print("deposit indexes",state.eth1_deposit_index,state.deposit_requests_start_index) - # set deposit_balance_to_consume to some initial amount to see its removal later on in the test + # set deposit_balance_to_consume to some initial amount state.deposit_balance_to_consume = amount # reset balance for assert state.balances[0] = 0 @@ -106,12 +113,13 @@ def test_pending_deposit_validator_withdrawn(spec, state): # balance increases because of withdraw assert state.balances[0] == amount + @with_electra_and_later @spec_state_test def test_pending_deposit_validator_exiting_but_not_withdrawn(spec, state): amount = spec.MIN_ACTIVATION_BALANCE - - withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + spec.hash(state.validators[0].pubkey)[1:] + hash = spec.hash(state.validators[0].pubkey)[1:] + withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + hash state.slot = spec.SLOTS_PER_EPOCH * 2 state.validators[0].withdrawal_credentials = withdrawal_credentials # set validator to be withdrawable by current epoch @@ -123,16 +131,15 @@ def test_pending_deposit_validator_exiting_but_not_withdrawn(spec, state): amount=amount, slot=spec.GENESIS_SLOT, )) - # set deposit_requests_start_index to something low so that we skip the bridge validation + # skip the bridge validation state.deposit_requests_start_index = 0 - print("deposit indexes",state.eth1_deposit_index,state.deposit_requests_start_index) - # set deposit_balance_to_consume to some initial amount to see its removal later on in the test + # set deposit_balance_to_consume to some initial amount state.deposit_balance_to_consume = amount yield from run_process_pending_deposits(spec, state) # deposit_balance_to_consume was reset to 0 assert state.deposit_balance_to_consume == 0 - # deposit was postponed and not processed + # deposit was postponed and not processed assert len(state.pending_deposits) == 1 @@ -149,7 +156,7 @@ def test_pending_deposit_min_activation_balance(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 + # No leftover deposit balance to consume assert state.deposit_balance_to_consume == 0 assert state.pending_deposits == [] @@ -186,9 +193,8 @@ def test_pending_deposit_balance_above_churn(spec, state): # deposit was above churn, balance hasn't changed assert state.balances[index] == pre_balance # deposit balance to consume is the full churn limit - assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit( - state - ) + wantedBalanceToConsume = spec.get_activation_exit_churn_limit(state) + assert state.deposit_balance_to_consume == wantedBalanceToConsume # deposit is still in the queue assert state.pending_deposits == [ build_pending_deposit_top_up(spec, state, index, amount) @@ -210,7 +216,7 @@ def test_pending_deposit_preexisting_churn(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 + # No leftover deposit balance to consume assert state.deposit_balance_to_consume == 0 # queue emptied assert state.pending_deposits == [] @@ -221,10 +227,12 @@ def test_pending_deposit_preexisting_churn(spec, state): def test_multiple_pending_deposits_below_churn(spec, state): amount = 10**9 state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, validator_index=0, amount=amount) + build_pending_deposit_top_up(spec, state, + validator_index=0, amount=amount) ) state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, validator_index=1, amount=amount) + build_pending_deposit_top_up(spec, state, + validator_index=1, amount=amount) ) pre_balances = state.balances.copy() @@ -232,7 +240,7 @@ def test_multiple_pending_deposits_below_churn(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 + # No leftover deposit balance to consume assert state.deposit_balance_to_consume == 0 assert state.pending_deposits == [] @@ -244,7 +252,8 @@ def test_multiple_pending_deposits_above_churn(spec, state): amount = (spec.get_activation_exit_churn_limit(state) // 3) + 1 for i in [0, 1, 2]: state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, validator_index=i, amount=amount) + build_pending_deposit_top_up(spec, state, + validator_index=i, amount=amount) ) pre_balances = state.balances.copy() @@ -261,7 +270,8 @@ def test_multiple_pending_deposits_above_churn(spec, state): ) # third deposit is still in the queue assert state.pending_deposits == [ - build_pending_deposit_top_up(spec, state, validator_index=2, amount=amount) + build_pending_deposit_top_up(spec, state, + validator_index=2, amount=amount) ] @@ -270,7 +280,10 @@ 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_deposits.append(build_pending_deposit_top_up(spec, state, validator_index=index, amount=amount)) + 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 @@ -280,7 +293,7 @@ def test_skipped_deposit_exiting_validator(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 + # All deposits either processed or postponed assert state.deposit_balance_to_consume == 0 # The deposit is still in the queue assert state.pending_deposits == pre_pending_deposits @@ -292,7 +305,10 @@ 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_deposits.append(build_pending_deposit_top_up(spec, state, validator_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) @@ -314,7 +330,10 @@ 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_deposits.append(build_pending_deposit_top_up(spec, state, validator_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) @@ -325,10 +344,13 @@ def test_multiple_pending_one_skipped(spec, state): for i in [0, 2]: assert state.balances[i] == pre_balances[i] + amount assert state.balances[1] == pre_balances[1] - # All deposits either processed or postponed, no leftover deposit balance to consume + # All deposits either processed or postponed assert state.deposit_balance_to_consume == 0 # second deposit is still in the queue - assert state.pending_deposits == [build_pending_deposit_top_up(spec, state, validator_index=1, amount=amount)] + assert state.pending_deposits == [ + build_pending_deposit_top_up(spec, state, + validator_index=1, amount=amount) + ] @with_electra_and_later @@ -338,8 +360,16 @@ 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_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)) + 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) @@ -352,11 +382,16 @@ def test_mixture_of_skipped_and_above_churn(spec, state): for i in [1, 2]: assert state.balances[i] == pre_balances[i] # First deposit consumes some deposit balance - # 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_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)] + # Deposit is not processed + wanted_balance = spec.get_activation_exit_churn_limit(state) - amount01 + assert state.deposit_balance_to_consume == wanted_balance + # second and third deposit still in the queue + 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 @@ -364,42 +399,55 @@ 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_deposits.append(build_pending_deposit_top_up(spec, state, validator_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) + withdrawable_epoch = state.validators[index].withdrawable_epoch + state.slot = spec.SLOTS_PER_EPOCH * (withdrawable_epoch + 1) 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 + # No leftover deposit balance to consume assert state.deposit_balance_to_consume == 0 assert state.pending_deposits == [] @with_electra_and_later @spec_state_test -def test_processing_deposit_of_withdrawable_validator_does_not_get_churned(spec, state): +def test_processing_deposit_of_withdrawable_validator_not_churned(spec, state): amount = spec.MAX_EFFECTIVE_BALANCE_ELECTRA for i in [0, 1]: - state.pending_deposits.append(build_pending_deposit_top_up(spec, state, validator_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) # Set epoch to withdrawable epoch + 1 to allow processing of the deposit - state.slot = spec.SLOTS_PER_EPOCH * (state.validators[0].withdrawable_epoch + 1) + withdraw_epoch = state.validators[0].withdrawable_epoch + state.slot = spec.SLOTS_PER_EPOCH * (withdraw_epoch + 1) # Don't use run_epoch_processing_with to avoid penalties being applied yield 'pre', state spec.process_pending_deposits(state) yield 'post', state - # First deposit is processed though above churn limit, because validator is withdrawable + # First deposit is processed though above churn limit assert state.balances[0] == pre_balances[0] + amount # Second deposit is not processed because above churn assert state.balances[1] == pre_balances[1] - # Second deposit is not processed, so there's leftover deposit balance to consume. + # Second deposit is not processed # First deposit does not consume any. - assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(state) - assert state.pending_deposits == [build_pending_deposit_top_up(spec, state, validator_index=1, amount=amount)] + wantedLimit = spec.get_activation_exit_churn_limit(state) + assert state.deposit_balance_to_consume == wantedLimit + assert state.pending_deposits == [ + build_pending_deposit_top_up(spec, state, + validator_index=1, amount=amount) + ] From 2c8dd39429517ad568845f8ba80c494ae45f32af Mon Sep 17 00:00:00 2001 From: james-prysm Date: Thu, 11 Jul 2024 15:15:23 -0500 Subject: [PATCH 021/103] updating tests --- .../test_apply_pending_deposit.py | 17 +++--- .../test_process_pending_deposits.py | 53 +++++++++++++++---- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index c5ed4aa3a7..43ad420247 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -4,6 +4,7 @@ ) 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 @with_electra_and_later @@ -69,7 +70,8 @@ def test_apply_pending_deposit_switch_to_compounding(spec, state): spec.COMPOUNDING_WITHDRAWAL_PREFIX + spec.hash(pubkeys[index])[1:] ) - state.slot = spec.SLOTS_PER_EPOCH * 2 + # 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 @@ -86,11 +88,11 @@ def test_apply_pending_deposit_switch_to_compounding(spec, state): slot=spec.GENESIS_SLOT, signature=deposit_data.signature, ) - state.balances[0] = 0 + state.balances[index] = 0 # run test spec.apply_pending_deposit(state, deposit) # validator balance should increase - assert state.balances[0] == amount + assert state.balances[index] == amount current_credentials = state.validators[0].withdrawal_credentials assert current_credentials == compounding_credentials @@ -124,10 +126,13 @@ def test_apply_pending_deposit_switch_to_compounding_not_exited(spec, state): slot=spec.GENESIS_SLOT, signature=deposit_data.signature, ) - state.balances[0] = 0 + state.balances[index] = 0 # run test spec.apply_pending_deposit(state, deposit) # validator balance should increase - assert state.balances[0] == amount + assert state.balances[index] == amount # make sure validator did not switch to compounding if not exited - assert state.validators[0].withdrawal_credentials == withdrawal_credentials + current_credentials = state.validators[0].withdrawal_credentials + assert current_credentials == withdrawal_credentials + # postpone pending_deposit + assert len(state.pending_deposits) == 0 diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 207bf89400..53bfd8979c 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -6,6 +6,10 @@ from eth2spec.test.helpers.deposits import ( build_pending_deposit_top_up, ) +from tests.core.pyspec.eth2spec.test.helpers.deposits import build_deposit_data +from eth2spec.test.helpers.state import next_epoch_via_block + +from eth2spec.test.helpers.keys import privkeys, pubkeys def run_process_pending_deposits(spec, state): @@ -87,17 +91,37 @@ def test_pending_deposit_not_finalized(spec, state): @spec_state_test def test_pending_deposit_validator_withdrawn(spec, state): amount = spec.MIN_ACTIVATION_BALANCE - hash = spec.hash(state.validators[0].pubkey)[1:] - withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + hash - state.slot = spec.SLOTS_PER_EPOCH * 2 - state.validators[0].withdrawal_credentials = withdrawal_credentials + 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 + previous_epoch = spec.get_current_epoch(state) - 1 + # set validator to be exited by current epoch + state.validators[index].exit_epoch = previous_epoch # set validator to be withdrawable by current epoch - state.validators[0].withdrawable_epoch = spec.get_current_epoch(state) - 1 + state.validators[index].withdrawable_epoch = previous_epoch + deposit_data = build_deposit_data(spec, + pubkeys[index], + privkeys[index], + amount, + compounding_credentials, + signed=True) + # set withdrawal credentials to compounding but should not switch since + # validator is already withdrawing state.pending_deposits.append(spec.PendingDeposit( - pubkey=state.validators[0].pubkey, - withdrawal_credentials=state.validators[0].withdrawal_credentials, + pubkey=pubkeys[index], + withdrawal_credentials=compounding_credentials, amount=amount, slot=spec.GENESIS_SLOT, + signature=deposit_data.signature, )) # skip the bridge validation state.deposit_requests_start_index = 0 @@ -105,13 +129,19 @@ def test_pending_deposit_validator_withdrawn(spec, state): state.deposit_balance_to_consume = amount # reset balance for assert state.balances[0] = 0 + old_validator_count = len(state.validators) yield from run_process_pending_deposits(spec, state) + btc = state.deposit_balance_to_consume # deposit_balance_to_consume was reset to 0 - assert state.deposit_balance_to_consume == 0 + assert btc == 0 # deposit was processed assert state.pending_deposits == [] # balance increases because of withdraw assert state.balances[0] == amount + # churn limit was not reached + assert not amount > spec.get_activation_exit_churn_limit(state) + # validator count should stay the same + assert len(state.validators) == old_validator_count @with_electra_and_later @@ -120,7 +150,8 @@ def test_pending_deposit_validator_exiting_but_not_withdrawn(spec, state): amount = spec.MIN_ACTIVATION_BALANCE hash = spec.hash(state.validators[0].pubkey)[1:] withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + hash - state.slot = spec.SLOTS_PER_EPOCH * 2 + # advance the state + next_epoch_via_block(spec, state) state.validators[0].withdrawal_credentials = withdrawal_credentials # set validator to be withdrawable by current epoch state.validators[0].exit_epoch = spec.get_current_epoch(state) - 1 @@ -445,8 +476,8 @@ def test_processing_deposit_of_withdrawable_validator_not_churned(spec, state): assert state.balances[1] == pre_balances[1] # Second deposit is not processed # First deposit does not consume any. - wantedLimit = spec.get_activation_exit_churn_limit(state) - assert state.deposit_balance_to_consume == wantedLimit + wanted_limit = spec.get_activation_exit_churn_limit(state) + assert state.deposit_balance_to_consume == wanted_limit assert state.pending_deposits == [ build_pending_deposit_top_up(spec, state, validator_index=1, amount=amount) From 320a692bec59ba707d2b9bbd442f582c9ca06b88 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Fri, 12 Jul 2024 14:11:52 -0500 Subject: [PATCH 022/103] adding test for chrun --- .../test_process_pending_deposits.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 53bfd8979c..c2c463dab6 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -209,6 +209,47 @@ def test_pending_deposit_balance_equal_churn(spec, state): assert state.pending_deposits == [] +@with_electra_and_later +@spec_state_test +def test_pending_deposit_balance_equal_churn_with_compounding(spec, state): + 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:] + ) + amount = spec.get_activation_exit_churn_limit(state) + state.validators[index].withdrawal_credentials = withdrawal_credentials + deposit_data = build_deposit_data(spec, + pubkeys[index], + privkeys[index], + amount, + compounding_credentials, + signed=True) + # set withdrawal credentials to compounding but should not switch since + # validator is already withdrawing + state.pending_deposits.append(spec.PendingDeposit( + pubkey=pubkeys[index], + withdrawal_credentials=compounding_credentials, + amount=amount, + slot=spec.GENESIS_SLOT, + signature=deposit_data.signature, + )) + pre_balance = state.balances[index] + + 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_deposits == [] + current_credentials = state.validators[0].withdrawal_credentials + # validator is not exited, so it should not switch to compounding + assert current_credentials == withdrawal_credentials + + @with_electra_and_later @spec_state_test def test_pending_deposit_balance_above_churn(spec, state): From 48b9928194099be110e18bf8392cb2f8ed1a6b10 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Fri, 12 Jul 2024 14:30:49 -0500 Subject: [PATCH 023/103] adding out of validator range test --- .../test_process_pending_deposits.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index c2c463dab6..f4f6b70fb9 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -15,6 +15,8 @@ def run_process_pending_deposits(spec, state): yield from run_epoch_processing_with( spec, state, 'process_pending_deposits') + + @with_electra_and_later @@ -174,6 +176,37 @@ def test_pending_deposit_validator_exiting_but_not_withdrawn(spec, state): assert len(state.pending_deposits) == 1 +@with_electra_and_later +@spec_state_test +def test_pending_deposit_not_in_validator_set(spec, state): + index = 2000 + amount = spec.MIN_ACTIVATION_BALANCE + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + spec.hash(pubkeys[index])[1:] + ) + deposit_data = build_deposit_data(spec, + pubkeys[index], + privkeys[index], + amount, + withdrawal_credentials, + signed=True) + state.pending_deposits.append(spec.PendingDeposit( + pubkey=pubkeys[index], + withdrawal_credentials=withdrawal_credentials, + amount=amount, + slot=spec.GENESIS_SLOT, + signature=deposit_data.signature, + )) + value_error = False + try: + yield from run_process_pending_deposits(spec, state) + except ValueError: + value_error = True + + assert value_error + + @with_electra_and_later @spec_state_test def test_pending_deposit_min_activation_balance(spec, state): From bf03d78a3aa6aa25d75884d0f38e835413a65ed2 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Fri, 12 Jul 2024 14:32:06 -0500 Subject: [PATCH 024/103] fixing linting --- .../electra/epoch_processing/test_process_pending_deposits.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index f4f6b70fb9..2ec57db975 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -15,8 +15,6 @@ def run_process_pending_deposits(spec, state): yield from run_epoch_processing_with( spec, state, 'process_pending_deposits') - - @with_electra_and_later From ea33eac282f2d4e1eaffe00188a2bd8c65d2e8e8 Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Fri, 26 Jul 2024 09:57:57 -0500 Subject: [PATCH 025/103] Update tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py Co-authored-by: Mikhail Kalinin --- .../test/electra/epoch_processing/test_apply_pending_deposit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index 43ad420247..bcf1af84f3 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -10,7 +10,7 @@ @with_electra_and_later @spec_state_test def test_apply_pending_deposit_add_validator_to_registry(spec, state): - amount = 100 + amount = spec.MIN_ACTIVATION_BALANCE # select validator set outside of the mainnet preset of 256 index = 2000 From 1feb1fbc0fc40f1f0248373ea24700f3a074ff4a Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Fri, 26 Jul 2024 09:58:25 -0500 Subject: [PATCH 026/103] Update tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py Co-authored-by: Mikhail Kalinin --- .../test/electra/epoch_processing/test_apply_pending_deposit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index bcf1af84f3..7f4ca30ecc 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -13,7 +13,7 @@ def test_apply_pending_deposit_add_validator_to_registry(spec, state): amount = spec.MIN_ACTIVATION_BALANCE # select validator set outside of the mainnet preset of 256 - index = 2000 + index = len(state.validators) withdrawal_credentials = ( spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkeys[index])[1:] ) From d62559fe90a80363ac3b0b88ee1fbb8f469dbd35 Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Fri, 26 Jul 2024 09:59:12 -0500 Subject: [PATCH 027/103] Update tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py Co-authored-by: Mikhail Kalinin --- .../test/electra/epoch_processing/test_apply_pending_deposit.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index 7f4ca30ecc..5dc1b3b1d8 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -40,7 +40,6 @@ def test_apply_pending_deposit_add_validator_to_registry(spec, state): @spec_state_test def test_apply_pending_deposit_increases_balance(spec, state): amount = 100 - state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH # signature doesn't matter here as it's interpreted as a top-up deposit = spec.PendingDeposit( pubkey=state.validators[0].pubkey, From 6903babf7d1ed90bedfcf0f33c938519844753b4 Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Fri, 26 Jul 2024 11:01:58 -0500 Subject: [PATCH 028/103] Update tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py Co-authored-by: Mikhail Kalinin --- .../test/electra/epoch_processing/test_apply_pending_deposit.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index 5dc1b3b1d8..d7ad5dd6d3 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -110,8 +110,6 @@ def test_apply_pending_deposit_switch_to_compounding_not_exited(spec, state): spec.COMPOUNDING_WITHDRAWAL_PREFIX + spec.hash(pubkeys[index])[1:] ) state.validators[index].withdrawal_credentials = withdrawal_credentials - # set validator to not be exited - state.validators[index].exit_epoch = spec.FAR_FUTURE_EPOCH deposit_data = build_deposit_data(spec, pubkeys[index], privkeys[index], From 4a4cdfc1e7fb6a213e9e65d9679ac5a55974665e Mon Sep 17 00:00:00 2001 From: james-prysm Date: Fri, 26 Jul 2024 11:35:59 -0500 Subject: [PATCH 029/103] revert beacon-chain.md changes --- specs/electra/beacon-chain.md | 182 +++++++++++++++++----------------- 1 file changed, 90 insertions(+), 92 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 5fdefeb93a..bce8ed2a39 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -8,98 +8,96 @@ -- [Electra -- The Beacon Chain](#electra----the-beacon-chain) - - [Table of contents](#table-of-contents) - - [Introduction](#introduction) - - [Constants](#constants) - - [Misc](#misc) - - [Withdrawal prefixes](#withdrawal-prefixes) - - [Domains](#domains) - - [Preset](#preset) - - [Gwei values](#gwei-values) - - [Rewards and penalties](#rewards-and-penalties) - - [State list lengths](#state-list-lengths) - - [Max operations per block](#max-operations-per-block) - - [Execution](#execution) - - [Withdrawals processing](#withdrawals-processing) - - [Pending deposits processing](#pending-deposits-processing) - - [Configuration](#configuration) - - [Validator cycle](#validator-cycle) - - [Containers](#containers) - - [New containers](#new-containers) - - [`DepositRequest`](#depositrequest) - - [`PendingDeposit`](#pendingdeposit) - - [`PendingPartialWithdrawal`](#pendingpartialwithdrawal) - - [`WithdrawalRequest`](#withdrawalrequest) - - [`ConsolidationRequest`](#consolidationrequest) - - [`PendingConsolidation`](#pendingconsolidation) - - [Modified Containers](#modified-containers) - - [`AttesterSlashing`](#attesterslashing) - - [Extended Containers](#extended-containers) - - [`Attestation`](#attestation) - - [`IndexedAttestation`](#indexedattestation) - - [`BeaconBlockBody`](#beaconblockbody) - - [`ExecutionPayload`](#executionpayload) - - [`ExecutionPayloadHeader`](#executionpayloadheader) - - [`BeaconState`](#beaconstate) - - [Helper functions](#helper-functions) - - [Predicates](#predicates) - - [Modified `compute_proposer_index`](#modified-compute_proposer_index) - - [Modified `is_eligible_for_activation_queue`](#modified-is_eligible_for_activation_queue) - - [New `is_compounding_withdrawal_credential`](#new-is_compounding_withdrawal_credential) - - [New `has_compounding_withdrawal_credential`](#new-has_compounding_withdrawal_credential) - - [New `has_execution_withdrawal_credential`](#new-has_execution_withdrawal_credential) - - [Modified `is_fully_withdrawable_validator`](#modified-is_fully_withdrawable_validator) - - [Modified `is_partially_withdrawable_validator`](#modified-is_partially_withdrawable_validator) - - [Misc](#misc-1) - - [New `get_committee_indices`](#new-get_committee_indices) - - [New `get_validator_max_effective_balance`](#new-get_validator_max_effective_balance) - - [Beacon state accessors](#beacon-state-accessors) - - [New `get_balance_churn_limit`](#new-get_balance_churn_limit) - - [New `get_activation_exit_churn_limit`](#new-get_activation_exit_churn_limit) - - [New `get_consolidation_churn_limit`](#new-get_consolidation_churn_limit) - - [New `get_active_balance`](#new-get_active_balance) - - [New `get_pending_balance_to_withdraw`](#new-get_pending_balance_to_withdraw) - - [Modified `get_attesting_indices`](#modified-get_attesting_indices) - - [Modified `get_next_sync_committee_indices`](#modified-get_next_sync_committee_indices) - - [Beacon state mutators](#beacon-state-mutators) - - [Modified `initiate_validator_exit`](#modified-initiate_validator_exit) - - [New `switch_to_compounding_validator`](#new-switch_to_compounding_validator) - - [New `queue_excess_active_balance`](#new-queue_excess_active_balance) - - [New `queue_entire_balance_and_reset_validator`](#new-queue_entire_balance_and_reset_validator) - - [New `compute_exit_epoch_and_update_churn`](#new-compute_exit_epoch_and_update_churn) - - [New `compute_consolidation_epoch_and_update_churn`](#new-compute_consolidation_epoch_and_update_churn) - - [Modified `slash_validator`](#modified-slash_validator) - - [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Epoch processing](#epoch-processing) - - [Modified `process_epoch`](#modified-process_epoch) - - [Modified `process_registry_updates`](#modified-process_registry_updates) - - [New `apply_pending_deposit`](#new-apply_pending_deposit) - - [New `process_pending_deposits`](#new-process_pending_deposits) - - [New `process_pending_consolidations`](#new-process_pending_consolidations) - - [Modified `process_effective_balance_updates`](#modified-process_effective_balance_updates) - - [Block processing](#block-processing) - - [Withdrawals](#withdrawals) - - [Modified `get_expected_withdrawals`](#modified-get_expected_withdrawals) - - [Modified `process_withdrawals`](#modified-process_withdrawals) - - [Execution payload](#execution-payload) - - [Modified `process_execution_payload`](#modified-process_execution_payload) - - [Operations](#operations) - - [Modified `process_operations`](#modified-process_operations) - - [Attestations](#attestations) - - [Modified `process_attestation`](#modified-process_attestation) - - [Deposits](#deposits) - - [Modified `apply_deposit`](#modified-apply_deposit) - - [New `is_valid_deposit_signature`](#new-is_valid_deposit_signature) - - [Voluntary exits](#voluntary-exits) - - [Modified `process_voluntary_exit`](#modified-process_voluntary_exit) - - [Execution layer withdrawal requests](#execution-layer-withdrawal-requests) - - [New `process_withdrawal_request`](#new-process_withdrawal_request) - - [Deposit requests](#deposit-requests) - - [New `process_deposit_request`](#new-process_deposit_request) - - [Execution layer consolidation requests](#execution-layer-consolidation-requests) - - [New `process_consolidation_request`](#new-process_consolidation_request) - - [Testing](#testing) +- [Introduction](#introduction) +- [Constants](#constants) + - [Misc](#misc) + - [Withdrawal prefixes](#withdrawal-prefixes) + - [Domains](#domains) +- [Preset](#preset) + - [Gwei values](#gwei-values) + - [Rewards and penalties](#rewards-and-penalties) + - [State list lengths](#state-list-lengths) + - [Max operations per block](#max-operations-per-block) + - [Execution](#execution) + - [Withdrawals processing](#withdrawals-processing) + - [Pending deposits processing](#pending-deposits-processing) +- [Configuration](#configuration) + - [Validator cycle](#validator-cycle) +- [Containers](#containers) + - [New containers](#new-containers) + - [`DepositRequest`](#depositrequest) + - [`PendingDeposit`](#pendingdeposit) + - [`PendingPartialWithdrawal`](#pendingpartialwithdrawal) + - [`WithdrawalRequest`](#withdrawalrequest) + - [`ConsolidationRequest`](#consolidationrequest) + - [`PendingConsolidation`](#pendingconsolidation) + - [Modified Containers](#modified-containers) + - [`AttesterSlashing`](#attesterslashing) + - [Extended Containers](#extended-containers) + - [`Attestation`](#attestation) + - [`IndexedAttestation`](#indexedattestation) + - [`BeaconBlockBody`](#beaconblockbody) + - [`ExecutionPayload`](#executionpayload) + - [`ExecutionPayloadHeader`](#executionpayloadheader) + - [`BeaconState`](#beaconstate) +- [Helper functions](#helper-functions) + - [Predicates](#predicates) + - [Modified `compute_proposer_index`](#modified-compute_proposer_index) + - [Modified `is_eligible_for_activation_queue`](#modified-is_eligible_for_activation_queue) + - [New `is_compounding_withdrawal_credential`](#new-is_compounding_withdrawal_credential) + - [New `has_compounding_withdrawal_credential`](#new-has_compounding_withdrawal_credential) + - [New `has_execution_withdrawal_credential`](#new-has_execution_withdrawal_credential) + - [Modified `is_fully_withdrawable_validator`](#modified-is_fully_withdrawable_validator) + - [Modified `is_partially_withdrawable_validator`](#modified-is_partially_withdrawable_validator) + - [Misc](#misc-1) + - [New `get_committee_indices`](#new-get_committee_indices) + - [New `get_validator_max_effective_balance`](#new-get_validator_max_effective_balance) + - [Beacon state accessors](#beacon-state-accessors) + - [New `get_balance_churn_limit`](#new-get_balance_churn_limit) + - [New `get_activation_exit_churn_limit`](#new-get_activation_exit_churn_limit) + - [New `get_consolidation_churn_limit`](#new-get_consolidation_churn_limit) + - [New `get_active_balance`](#new-get_active_balance) + - [New `get_pending_balance_to_withdraw`](#new-get_pending_balance_to_withdraw) + - [Modified `get_attesting_indices`](#modified-get_attesting_indices) + - [Modified `get_next_sync_committee_indices`](#modified-get_next_sync_committee_indices) + - [Beacon state mutators](#beacon-state-mutators) + - [Modified `initiate_validator_exit`](#modified-initiate_validator_exit) + - [New `switch_to_compounding_validator`](#new-switch_to_compounding_validator) + - [New `queue_excess_active_balance`](#new-queue_excess_active_balance) + - [New `queue_entire_balance_and_reset_validator`](#new-queue_entire_balance_and_reset_validator) + - [New `compute_exit_epoch_and_update_churn`](#new-compute_exit_epoch_and_update_churn) + - [New `compute_consolidation_epoch_and_update_churn`](#new-compute_consolidation_epoch_and_update_churn) + - [Modified `slash_validator`](#modified-slash_validator) +- [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Epoch processing](#epoch-processing) + - [Modified `process_epoch`](#modified-process_epoch) + - [Modified `process_registry_updates`](#modified-process_registry_updates) + - [New `apply_pending_deposit`](#new-apply_pending_deposit) + - [New `process_pending_deposits`](#new-process_pending_deposits) + - [New `process_pending_consolidations`](#new-process_pending_consolidations) + - [Modified `process_effective_balance_updates`](#modified-process_effective_balance_updates) + - [Block processing](#block-processing) + - [Withdrawals](#withdrawals) + - [Modified `get_expected_withdrawals`](#modified-get_expected_withdrawals) + - [Modified `process_withdrawals`](#modified-process_withdrawals) + - [Execution payload](#execution-payload) + - [Modified `process_execution_payload`](#modified-process_execution_payload) + - [Operations](#operations) + - [Modified `process_operations`](#modified-process_operations) + - [Attestations](#attestations) + - [Modified `process_attestation`](#modified-process_attestation) + - [Deposits](#deposits) + - [Modified `apply_deposit`](#modified-apply_deposit) + - [New `is_valid_deposit_signature`](#new-is_valid_deposit_signature) + - [Voluntary exits](#voluntary-exits) + - [Modified `process_voluntary_exit`](#modified-process_voluntary_exit) + - [Execution layer withdrawal requests](#execution-layer-withdrawal-requests) + - [New `process_withdrawal_request`](#new-process_withdrawal_request) + - [Deposit requests](#deposit-requests) + - [New `process_deposit_request`](#new-process_deposit_request) + - [Execution layer consolidation requests](#execution-layer-consolidation-requests) + - [New `process_consolidation_request`](#new-process_consolidation_request) +- [Testing](#testing) From e80c39b1edb4ada4a61f07aef42c0a117746e534 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 29 Jul 2024 09:32:18 -0500 Subject: [PATCH 030/103] mikhail's feedback --- .../test/electra/epoch_processing/test_apply_pending_deposit.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index d7ad5dd6d3..33dc775a51 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -87,7 +87,6 @@ def test_apply_pending_deposit_switch_to_compounding(spec, state): slot=spec.GENESIS_SLOT, signature=deposit_data.signature, ) - state.balances[index] = 0 # run test spec.apply_pending_deposit(state, deposit) # validator balance should increase From 6a7bfe9b341baa5db9e8426430d16667a71bb467 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 29 Jul 2024 10:49:57 -0500 Subject: [PATCH 031/103] removed wrong balance --- .../electra/epoch_processing/test_apply_pending_deposit.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index 33dc775a51..a84153b514 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -47,8 +47,6 @@ def test_apply_pending_deposit_increases_balance(spec, state): amount=amount, slot=spec.GENESIS_SLOT ) - # reset the balance - state.balances[0] = 0 # run test spec.apply_pending_deposit(state, deposit) assert state.balances[0] == amount @@ -87,6 +85,7 @@ def test_apply_pending_deposit_switch_to_compounding(spec, state): slot=spec.GENESIS_SLOT, signature=deposit_data.signature, ) + state.balances[index] = 0 # run test spec.apply_pending_deposit(state, deposit) # validator balance should increase From 824e37467928ff91c67f3544d810387cac673204 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 30 Jul 2024 10:00:47 -0500 Subject: [PATCH 032/103] applying feedback fir add validator test --- .../epoch_processing/test_apply_pending_deposit.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index a84153b514..4de4f624e2 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -1,3 +1,4 @@ +from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with from eth2spec.test.context import ( spec_state_test, with_electra_and_later, @@ -7,6 +8,11 @@ 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') + + @with_electra_and_later @spec_state_test def test_apply_pending_deposit_add_validator_to_registry(spec, state): @@ -31,7 +37,8 @@ def test_apply_pending_deposit_add_validator_to_registry(spec, state): signature=deposit_data.signature, ) old_validator_count = len(state.validators) - spec.apply_pending_deposit(state, deposit) + 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 From 0a51fe032d9d4a0b5fef6b67bb46234edba56579 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Fri, 2 Aug 2024 16:46:16 -0500 Subject: [PATCH 033/103] updating tests --- .../test_process_pending_deposits.py | 165 ++++++++++++------ .../pyspec/eth2spec/test/helpers/deposits.py | 36 ++++ 2 files changed, 151 insertions(+), 50 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 2ec57db975..78fe4463b8 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -4,12 +4,12 @@ with_electra_and_later, ) from eth2spec.test.helpers.deposits import ( + build_pending_deposit, build_pending_deposit_top_up, ) -from tests.core.pyspec.eth2spec.test.helpers.deposits import build_deposit_data from eth2spec.test.helpers.state import next_epoch_via_block -from eth2spec.test.helpers.keys import privkeys, pubkeys +from eth2spec.test.helpers.keys import pubkeys def run_process_pending_deposits(spec, state): @@ -17,6 +17,59 @@ def run_process_pending_deposits(spec, state): spec, state, 'process_pending_deposits') +@with_electra_and_later +@spec_state_test +def test_pending_deposit_under_max(spec, state): + # pick an amount that adds to less than churn limit + amount = 100 + undermax = spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING - 1 + for i in range(undermax): + print(i) + state.pending_deposits.append( + spec.PendingDeposit( + pubkey=state.validators[i].pubkey, + withdrawal_credentials=( + state.validators[i].withdrawal_credentials + ), + amount=amount, + slot=spec.GENESIS_SLOT, + ) + ) + assert len(state.pending_deposits) == undermax, \ + "pending deposits is not undermax" + yield from run_process_pending_deposits(spec, state) + assert len(state.pending_deposits) == 0 + assert state.balances[0] == amount + spec.MIN_ACTIVATION_BALANCE + assert state.balances[undermax - 1] == amount + spec.MIN_ACTIVATION_BALANCE + assert state.balances[undermax] == spec.MIN_ACTIVATION_BALANCE + + +@with_electra_and_later +@spec_state_test +def test_pending_deposit_max(spec, state): + # pick an amount that adds to less than churn limit + amount = 100 + max = spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING + for i in range(max): + state.pending_deposits.append( + spec.PendingDeposit( + pubkey=state.validators[i].pubkey, + withdrawal_credentials=( + state.validators[i].withdrawal_credentials + ), + amount=amount, + slot=spec.GENESIS_SLOT, + ) + ) + assert len(state.pending_deposits) == max, \ + "pending deposits is not max" + yield from run_process_pending_deposits(spec, state) + assert len(state.pending_deposits) == 0 + assert state.balances[0] == amount + spec.MIN_ACTIVATION_BALANCE + assert state.balances[max - 1] == amount + spec.MIN_ACTIVATION_BALANCE + assert state.balances[max] == spec.MIN_ACTIVATION_BALANCE + + @with_electra_and_later @spec_state_test def test_pending_deposit_over_max(spec, state): @@ -36,10 +89,47 @@ def test_pending_deposit_over_max(spec, state): ) assert len(state.pending_deposits) == overmax, \ "pending deposits is not overmax" - # the remaining deposit over MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING - # should remain in pending_deposits yield from run_process_pending_deposits(spec, state) assert len(state.pending_deposits) == 1 + assert state.balances[overmax - 1] == spec.MIN_ACTIVATION_BALANCE + amount + + +@with_electra_and_later +@spec_state_test +def test_new_deposit_eth1_withdrawal_credentials(spec, state): + state.validators[0].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 + + state.pending_deposits.append(spec.PendingDeposit( + pubkey=state.validators[0].pubkey, + withdrawal_credentials=state.validators[0].withdrawal_credentials, + amount=amount, + slot=1, + )) + + 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): + state.validators[0].withdrawal_credentials = ( + b'\xFF' # Non specified withdrawal credentials version + + b'\x02' * 31 # Garabage bytes + ) + amount = spec.MAX_EFFECTIVE_BALANCE + state.pending_deposits.append(spec.PendingDeposit( + pubkey=state.validators[0].pubkey, + withdrawal_credentials=state.validators[0].withdrawal_credentials, + amount=amount, + slot=1, + )) + + yield from run_process_pending_deposits(spec, state) @with_electra_and_later @@ -108,21 +198,13 @@ def test_pending_deposit_validator_withdrawn(spec, state): state.validators[index].exit_epoch = previous_epoch # set validator to be withdrawable by current epoch state.validators[index].withdrawable_epoch = previous_epoch - deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - compounding_credentials, - signed=True) # set withdrawal credentials to compounding but should not switch since # validator is already withdrawing - state.pending_deposits.append(spec.PendingDeposit( - pubkey=pubkeys[index], - withdrawal_credentials=compounding_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - signature=deposit_data.signature, - )) + pd = build_pending_deposit(spec, index, + amount=amount, + withdrawal_credentials=compounding_credentials, + signed=True) + state.pending_deposits.append(pd) # skip the bridge validation state.deposit_requests_start_index = 0 # set deposit_balance_to_consume to some initial amount @@ -179,30 +261,20 @@ def test_pending_deposit_validator_exiting_but_not_withdrawn(spec, state): def test_pending_deposit_not_in_validator_set(spec, state): index = 2000 amount = spec.MIN_ACTIVATION_BALANCE - withdrawal_credentials = ( + wc = ( spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + spec.hash(pubkeys[index])[1:] ) - deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - withdrawal_credentials, - signed=True) - state.pending_deposits.append(spec.PendingDeposit( - pubkey=pubkeys[index], - withdrawal_credentials=withdrawal_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - signature=deposit_data.signature, - )) - value_error = False - try: - yield from run_process_pending_deposits(spec, state) - except ValueError: - value_error = True + pd = build_pending_deposit(spec, index, + amount=amount, + withdrawal_credentials=wc, + signed=True) - assert value_error + state.pending_deposits.append(pd) + old_length = len(state.validators) + yield from run_process_pending_deposits(spec, state) + # new validator activated + assert len(state.validators) == old_length + 1 @with_electra_and_later @@ -254,21 +326,14 @@ def test_pending_deposit_balance_equal_churn_with_compounding(spec, state): ) amount = spec.get_activation_exit_churn_limit(state) state.validators[index].withdrawal_credentials = withdrawal_credentials - deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - compounding_credentials, - signed=True) + # set withdrawal credentials to compounding but should not switch since # validator is already withdrawing - state.pending_deposits.append(spec.PendingDeposit( - pubkey=pubkeys[index], - withdrawal_credentials=compounding_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - signature=deposit_data.signature, - )) + pd = build_pending_deposit(spec, index, + amount=amount, + withdrawal_credentials=compounding_credentials, + signed=True) + state.pending_deposits.append(pd) pre_balance = state.balances[index] yield from run_process_pending_deposits(spec, state) diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index ae50f6b38a..fc6e6b5d27 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -235,6 +235,42 @@ def build_pending_deposit_top_up(spec, state, validator_index, amount, slot=None slot=slot, ) + +def build_pending_deposit(spec, validator_index, amount, + index=None, + pubkey=None, + privkey=None, + withdrawal_credentials=None, + slot=None, + signed=False): + if index is None: + index = validator_index + + if pubkey is None: + pubkey = pubkeys[validator_index] + + if privkey is None: + privkey = privkeys[validator_index] + + if slot is None: + slot = spec.GENESIS_SLOT + + pending_deposit = spec.PendingDeposit( + pubkey=pubkeys[index], + withdrawal_credentials=withdrawal_credentials, + amount=amount, + slot=slot, + ) + if signed: + deposit_data = build_deposit_data(spec, + pubkeys[index], + privkeys[index], + amount, + withdrawal_credentials, + signed=True) + pending_deposit.signature = deposit_data.signature + return pending_deposit + # # Run processing # From 1afa51420edcbb6698ab59b12a208bb988f9a228 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Fri, 2 Aug 2024 17:21:48 -0500 Subject: [PATCH 034/103] adding test_process_pending_deposits_limit_is_reached --- .../test_process_pending_deposits.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 78fe4463b8..89da0ea2bb 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -177,6 +177,27 @@ def test_pending_deposit_not_finalized(spec, state): assert len(state.pending_deposits) == 1 +@with_electra_and_later +@spec_state_test +def test_process_pending_deposits_limit_is_reached(spec, state): + amount = 5000 + cumulative_amount = 0 + # set pending deposits to the maximum + for i in range(spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING+1): + wc = state.validators[i].withdrawal_credentials + pd = build_pending_deposit(spec, i, + amount=amount, + withdrawal_credentials=wc, + signed=True) + state.pending_deposits.append(pd) + cumulative_amount += amount + # churn limit was not reached + assert cumulative_amount < spec.get_activation_exit_churn_limit(state) + yield from run_process_pending_deposits(spec, state) + # no deposits above limit were processed + assert len(state.pending_deposits) == 1 + + @with_electra_and_later @spec_state_test def test_pending_deposit_validator_withdrawn(spec, state): From 336c6f15101e150907b6533be84f675ac702f939 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 5 Aug 2024 14:14:03 -0500 Subject: [PATCH 035/103] adding some more tests based on test plan --- .../test_apply_pending_deposit.py | 43 +++++++++++++++++++ .../test_process_pending_deposits.py | 26 +++++++++++ 2 files changed, 69 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index 4de4f624e2..0cf9cd3a95 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -101,6 +101,49 @@ def test_apply_pending_deposit_switch_to_compounding(spec, state): assert current_credentials == compounding_credentials +@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): diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 89da0ea2bb..05e8de00b5 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -154,6 +154,32 @@ def test_pending_deposit_eth1_bridge_not_applied(spec, state): assert len(state.pending_deposits) == 1 +@with_electra_and_later +@spec_state_test +def test_no_pending_deposit_eth1_bridge(spec, state): + amount = spec.MIN_ACTIVATION_BALANCE + # there is no pending eth1 bridge deposits + # state.eth1_deposit_index == state.deposit_requests_start_index + state.deposit_requests_start_index = state.eth1_deposit_index + index = 0 + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + spec.hash(pubkeys[index])[1:] + ) + pd = build_pending_deposit(spec, index, + amount=amount, + withdrawal_credentials=withdrawal_credentials, + signed=True) + state.pending_deposits.append(pd) + # set deposit_balance_to_consume to some initial amount + state.deposit_balance_to_consume = amount + yield from run_process_pending_deposits(spec, state) + # deposit_balance_to_consume was reset to 0 + assert state.deposit_balance_to_consume == 0 + # all pending deposits processed + assert len(state.pending_deposits) == 0 + + @with_electra_and_later @spec_state_test def test_pending_deposit_not_finalized(spec, state): From 3593a0cb3451a3c60ae37092594d4ef07207fa93 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 5 Aug 2024 16:38:51 -0500 Subject: [PATCH 036/103] adding 3 more tests --- .../test_apply_pending_deposit.py | 39 +++++++ .../test_process_pending_deposits.py | 108 ++++++++++++++++++ 2 files changed, 147 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index 0cf9cd3a95..3b8588ecce 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -101,6 +101,45 @@ def test_apply_pending_deposit_switch_to_compounding(spec, state): assert current_credentials == compounding_credentials +@with_electra_and_later +@spec_state_test +def test_apply_pending_deposit_switch_to_compounding_has_bls(spec, state): + amount = 100 + + # 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 + + @with_electra_and_later @spec_state_test def test_apply_pending_deposit_switch_to_compounding_invalid_sig(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 05e8de00b5..5babd66c86 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -393,6 +393,71 @@ def test_pending_deposit_balance_equal_churn_with_compounding(spec, state): assert current_credentials == withdrawal_credentials +@with_electra_and_later +@spec_state_test +def test_top_up__zero_balance(spec, state): + validator_index = 0 + amount = spec.MAX_EFFECTIVE_BALANCE // 4 + + initial_balance = 0 + initial_effective_balance = 0 + state.balances[validator_index] = initial_balance + val = state.validators[validator_index] + val.effective_balance = initial_effective_balance + wc = val.withdrawal_credentials + pd = build_pending_deposit(spec, validator_index, + amount=amount, + withdrawal_credentials=wc, + signed=True) + state.pending_deposits.append(pd) + + yield from run_process_pending_deposits(spec, state) + + deposits_len = len(state.pending_deposits) + assert state.pending_deposits[deposits_len - 1].amount == amount + # unchanged effective balance + assert val.effective_balance == initial_effective_balance + + +@with_electra_and_later +@spec_state_test +def test_incorrect_sig_top_up(spec, state): + validator_index = 0 + amount = spec.MAX_EFFECTIVE_BALANCE // 4 + + val = state.validators[validator_index] + wc = val.withdrawal_credentials + pd = build_pending_deposit(spec, validator_index, + amount=amount, + withdrawal_credentials=wc, + signed=False) + state.pending_deposits.append(pd) + + 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 + + initial_balance = 0 + initial_effective_balance = 0 + state.balances[validator_index] = initial_balance + val = state.validators[validator_index] + val.effective_balance = initial_effective_balance + wc = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(b"junk")[1:] + + pd = build_pending_deposit(spec, validator_index, + amount=amount, + withdrawal_credentials=wc, + signed=True) + state.pending_deposits.append(pd) + + yield from run_process_pending_deposits(spec, state) + + @with_electra_and_later @spec_state_test def test_pending_deposit_balance_above_churn(spec, state): @@ -416,6 +481,49 @@ def test_pending_deposit_balance_above_churn(spec, state): ] +@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 + wc = state.validators[validator_index].withdrawal_credentials + pd = build_pending_deposit(spec, validator_index, + amount=amount, + withdrawal_credentials=wc, + signed=True) + state.pending_deposits.append(pd) + + state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE + state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE + + yield from run_process_pending_deposits(spec, state) + + 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 + wc = state.validators[validator_index].withdrawal_credentials + pd = build_pending_deposit(spec, validator_index, + amount=amount, + withdrawal_credentials=wc, + signed=True) + state.pending_deposits.append(pd) + + 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_process_pending_deposits(spec, state) + + # unchanged effective balance + assert state.validators[validator_index].effective_balance == initial_effective_balance + + @with_electra_and_later @spec_state_test def test_pending_deposit_preexisting_churn(spec, state): From 2cf7de96cdca2d53c8fd1f56dbe024ca9991a9c7 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 6 Aug 2024 11:44:43 -0500 Subject: [PATCH 037/103] updating bridge tests --- .../test_process_pending_deposits.py | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 5babd66c86..8b502c60fe 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -132,6 +132,33 @@ def test_new_deposit_non_versioned_withdrawal_credentials(spec, state): yield from run_process_pending_deposits(spec, state) +@with_electra_and_later +@spec_state_test +def test_pending_deposit_eth1_bridge_pending(spec, state): + amount = spec.MIN_ACTIVATION_BALANCE + # There are pending Eth1 bridge deposits + # state.eth1_deposit_index < state.deposit_requests_start_index + state.deposit_requests_start_index = state.eth1_deposit_index + 1 + index = 0 + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + spec.hash(pubkeys[index])[1:] + ) + pd = build_pending_deposit(spec, index, + amount=amount, + withdrawal_credentials=withdrawal_credentials, + signed=True, + slot=1) + state.pending_deposits.append(pd) + # set deposit_balance_to_consume to some initial amount + state.deposit_balance_to_consume = amount + yield from run_process_pending_deposits(spec, state) + # deposit_balance_to_consume was reset to 0 + assert state.deposit_balance_to_consume == 0 + # deposit was postponed and not processed + assert len(state.pending_deposits) == 1 + + @with_electra_and_later @spec_state_test def test_pending_deposit_eth1_bridge_not_applied(spec, state): @@ -156,7 +183,7 @@ def test_pending_deposit_eth1_bridge_not_applied(spec, state): @with_electra_and_later @spec_state_test -def test_no_pending_deposit_eth1_bridge(spec, state): +def test_pending_deposit_no_eth1_bridge_pending(spec, state): amount = spec.MIN_ACTIVATION_BALANCE # there is no pending eth1 bridge deposits # state.eth1_deposit_index == state.deposit_requests_start_index From 337726cbaea061e30541280acb88ec453220563e Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 6 Aug 2024 12:04:46 -0500 Subject: [PATCH 038/103] adding no compounding test --- .../test_apply_pending_deposit.py | 42 +++++++++++++++++++ .../test_process_pending_deposits.py | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index 3b8588ecce..942fe9ab72 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -101,6 +101,48 @@ def test_apply_pending_deposit_switch_to_compounding(spec, state): 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 + 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 + + @with_electra_and_later @spec_state_test def test_apply_pending_deposit_switch_to_compounding_has_bls(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 8b502c60fe..aecfbd35d1 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -137,7 +137,7 @@ def test_new_deposit_non_versioned_withdrawal_credentials(spec, state): def test_pending_deposit_eth1_bridge_pending(spec, state): amount = spec.MIN_ACTIVATION_BALANCE # There are pending Eth1 bridge deposits - # state.eth1_deposit_index < state.deposit_requests_start_index + # state.eth1_deposit_index < state.deposit_requests_start_index state.deposit_requests_start_index = state.eth1_deposit_index + 1 index = 0 withdrawal_credentials = ( From 56fe31031ceb6b162a2d2a7e0d089c3d137e424e Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 6 Aug 2024 14:10:23 -0500 Subject: [PATCH 039/103] adding run_process_pending_deposits to deposit request tests to properly test changes --- .../test_process_deposit_request.py | 15 ++++ .../test_process_pending_deposits.py | 70 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py index 737fbcabb1..559b1c1bd1 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py @@ -6,6 +6,12 @@ ) 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') @with_electra_and_later @@ -18,6 +24,7 @@ def test_new_deposit_under_max(spec, state): 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 @@ -30,6 +37,7 @@ def test_new_deposit_max(spec, state): 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 @@ -42,6 +50,7 @@ def test_new_deposit_over_max(spec, state): 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 @@ -64,6 +73,7 @@ def test_new_deposit_eth1_withdrawal_credentials(spec, state): ) yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) + yield from run_process_pending_deposits(spec, state) @with_electra_and_later @@ -85,6 +95,7 @@ def test_new_deposit_non_versioned_withdrawal_credentials(spec, state): ) yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) + yield from run_process_pending_deposits(spec, state) @with_electra_and_later @@ -177,6 +188,7 @@ def test_incorrect_sig_top_up(spec, state): # 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 @@ -194,6 +206,7 @@ def test_incorrect_withdrawal_credentials_top_up(spec, state): # 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 @@ -208,6 +221,7 @@ def test_key_validate_invalid_subgroup(spec, state): 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 @@ -224,6 +238,7 @@ def test_key_validate_invalid_decompression(spec, state): 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 diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index aecfbd35d1..6d4b903545 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -1,6 +1,7 @@ from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with from eth2spec.test.context import ( spec_state_test, + always_bls, with_electra_and_later, ) from eth2spec.test.helpers.deposits import ( @@ -801,3 +802,72 @@ def test_processing_deposit_of_withdrawable_validator_not_churned(spec, state): build_pending_deposit_top_up(spec, state, validator_index=1, amount=amount) ] + + +@with_electra_and_later +@spec_state_test +@always_bls +def test_correct_sig_but_forked_state(spec, state): + amount = spec.MAX_EFFECTIVE_BALANCE + # deposits will always be valid, regardless of the current fork + state.fork.current_version = spec.Version('0x1234abcd') + index = 0 + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + spec.hash(pubkeys[index])[1:] + ) + wc = withdrawal_credentials + pd = build_pending_deposit(spec, index, + amount=amount, + withdrawal_credentials=wc, + signed=True) + state.pending_deposits.append(pd) + yield from run_process_pending_deposits(spec, state) + + +@with_electra_and_later +@spec_state_test +def test_key_validate_invalid_subgroup(spec, state): + amount = spec.MAX_EFFECTIVE_BALANCE + + # All-zero pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception. + pubkey = b'\x00' * 48 + + index = 0 + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + spec.hash(pubkey)[1:] + ) + wc = withdrawal_credentials + pd = build_pending_deposit(spec, index, + amount=amount, + withdrawal_credentials=wc, + pubkey=pubkey, + signed=True) + state.pending_deposits.append(pd) + yield from run_process_pending_deposits(spec, state) + + +@with_electra_and_later +@spec_state_test +def test_key_validate_invalid_decompression(spec, state): + 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) + + index = 0 + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + spec.hash(pubkey)[1:] + ) + wc = withdrawal_credentials + pd = build_pending_deposit(spec, index, + amount=amount, + withdrawal_credentials=wc, + pubkey=pubkey, + signed=True) + state.pending_deposits.append(pd) + yield from run_process_pending_deposits(spec, state) From c5db6dfcfb509462d9bc870847e0cbab196d7174 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 6 Aug 2024 14:36:20 -0500 Subject: [PATCH 040/103] adding fork version tests --- .../test_process_pending_deposits.py | 65 +++++++++++++++++++ .../pyspec/eth2spec/test/helpers/deposits.py | 13 ++-- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 6d4b903545..8f27c7db10 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -871,3 +871,68 @@ def test_key_validate_invalid_decompression(spec, state): signed=True) state.pending_deposits.append(pd) yield from run_process_pending_deposits(spec, state) + + +@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`. + assert state.fork.previous_version != state.fork.current_version + amount = spec.MAX_EFFECTIVE_BALANCE + index = 0 + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + spec.hash(pubkeys[index])[1:] + ) + wc = withdrawal_credentials + pd = build_pending_deposit(spec, index, + amount=amount, + withdrawal_credentials=wc, + fork_version=state.fork.previous_version, + signed=True) + state.pending_deposits.append(pd) + yield from run_process_pending_deposits(spec, state) + + +@with_electra_and_later +@spec_state_test +@always_bls +def test_apply_pending_deposit_with_genesis_fork_version(spec, state): + assert spec.config.GENESIS_FORK_VERSION not in (state.fork.previous_version, state.fork.current_version) + + assert state.fork.previous_version != state.fork.current_version + amount = spec.MAX_EFFECTIVE_BALANCE + index = 0 + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + spec.hash(pubkeys[index])[1:] + ) + wc = withdrawal_credentials + pd = build_pending_deposit(spec, index, + amount=amount, + withdrawal_credentials=wc, + fork_version=spec.GENESIS_FORK_VERSION, + signed=True) + state.pending_deposits.append(pd) + yield from run_process_pending_deposits(spec, state) + + +@with_electra_and_later +@spec_state_test +@always_bls +def test_apply_pending_deposit_with_bad_fork_version(spec, state): + amount = spec.MAX_EFFECTIVE_BALANCE + index = 0 + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + spec.hash(pubkeys[index])[1:] + ) + wc = withdrawal_credentials + pd = build_pending_deposit(spec, index, + amount=amount, + withdrawal_credentials=wc, + fork_version=spec.Version('0xAaBbCcDd'), + signed=True) + state.pending_deposits.append(pd) + yield from run_process_pending_deposits(spec, state) diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index fc6e6b5d27..72311be22f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -23,23 +23,26 @@ 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, signed=False): +def build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, fork_version, signed=False): deposit_data = spec.DepositData( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount, ) if signed: - sign_deposit_data(spec, deposit_data, privkey) + sign_deposit_data(spec, deposit_data, privkey, fork_version) return deposit_data -def sign_deposit_data(spec, deposit_data, privkey): +def sign_deposit_data(spec, deposit_data, privkey, fork_version): deposit_message = spec.DepositMessage( pubkey=deposit_data.pubkey, withdrawal_credentials=deposit_data.withdrawal_credentials, amount=deposit_data.amount) - domain = spec.compute_domain(spec.DOMAIN_DEPOSIT) + if fork_version is not None: + domain = spec.compute_domain(domain_type=spec.DOMAIN_DEPOSIT, fork_version=fork_version) + else: + domain = spec.compute_domain(spec.DOMAIN_DEPOSIT) signing_root = spec.compute_signing_root(deposit_message, domain) deposit_data.signature = bls.Sign(privkey, signing_root) @@ -241,6 +244,7 @@ def build_pending_deposit(spec, validator_index, amount, pubkey=None, privkey=None, withdrawal_credentials=None, + fork_version=None, slot=None, signed=False): if index is None: @@ -267,6 +271,7 @@ def build_pending_deposit(spec, validator_index, amount, privkeys[index], amount, withdrawal_credentials, + fork_version, signed=True) pending_deposit.signature = deposit_data.signature return pending_deposit From ada552461039382950ae98b5e1a204c0d2df6773 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 23 Aug 2024 14:32:50 +0600 Subject: [PATCH 041/103] Use the right MaxEB to create validator from deposit --- specs/electra/beacon-chain.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index fb11c463ac..e6908d6bda 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -87,6 +87,7 @@ - [Attestations](#attestations) - [Modified `process_attestation`](#modified-process_attestation) - [Deposits](#deposits) + - [Modified `get_validator_from_deposit`](#modified-get_validator_from_deposit) - [Modified `apply_deposit`](#modified-apply_deposit) - [New `is_valid_deposit_signature`](#new-is_valid_deposit_signature) - [Voluntary exits](#voluntary-exits) @@ -1268,6 +1269,31 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: ##### Deposits +###### Modified `get_validator_from_deposit` + +*Note*: The function is modified to use `MAX_EFFECTIVE_BALANCE_ELECTRA` for compounding withdrawal credential. + +```python +def get_validator_from_deposit(pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64) -> Validator: + if is_compounding_withdrawal_credential(withdrawal_credentials): + max_effective_balance = MAX_EFFECTIVE_BALANCE_ELECTRA + else: + max_effective_balance = MIN_ACTIVATION_BALANCE + + # [Modified in Electra:EIP7251] + effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, max_effective_balance) + + 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=effective_balance, + ) +``` + ###### Modified `apply_deposit` *Note*: The function `process_deposit` is modified to support EIP7251. From 97e6166bf1ee1f1c0fdc24890844ea675bf0e7d5 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 26 Aug 2024 16:17:53 +0600 Subject: [PATCH 042/103] Move switch to compounding to process_deposit_request --- specs/electra/beacon-chain.md | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index d3950dc480..caa1ec42ab 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -889,20 +889,6 @@ def apply_pending_deposit(state: BeaconState, deposit: PendingDeposit) -> None: validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey)) # Increase balance increase_balance(state, validator_index, deposit.amount) - # If validator has not exited, check if valid deposit switch to compounding credentials. - validator = state.validators[validator_index] - if ( - validator.exit_epoch < get_current_epoch(state) - and 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) ``` #### New `process_pending_deposits` @@ -1453,6 +1439,7 @@ 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 + # Create pending deposit state.pending_deposits.append(PendingDeposit( pubkey=deposit_request.pubkey, withdrawal_credentials=deposit_request.withdrawal_credentials, @@ -1460,6 +1447,24 @@ def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) signature=deposit_request.signature, slot=state.slot, )) + + # If validator is active, check switch to compounding + validator_pubkeys = [v.pubkey for v in state.validators] + if deposit_request.pubkey in validator_pubkeys: + validator_index = ValidatorIndex(validator_pubkeys.index(deposit_request.pubkey)) + validator = state.validators[validator_index] + if ( + validator.exit_epoch > get_current_epoch(state) + and is_compounding_withdrawal_credential(deposit_request.withdrawal_credentials) + and has_eth1_withdrawal_credential(validator) + and is_valid_deposit_signature( + deposit_request.pubkey, + deposit_request.withdrawal_credentials, + deposit_request.amount, + deposit_request.signature + ) + ): + switch_to_compounding_validator(state, validator_index) ``` ##### Execution layer consolidation requests From 990de16c0fe5a4209e38935ca8de52884a2ba9a9 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 26 Aug 2024 16:18:53 +0600 Subject: [PATCH 043/103] Add more deposit tests --- .../test_process_deposit_request.py | 426 +++++++----- .../test_apply_pending_deposit.py | 611 ++++++++++++------ .../pyspec/eth2spec/test/helpers/deposits.py | 204 ++++-- .../eth2spec/test/helpers/epoch_processing.py | 4 +- .../test_process_effective_balance_updates.py | 14 + 5 files changed, 817 insertions(+), 442 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py index 559b1c1bd1..1e78129034 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py @@ -2,307 +2,383 @@ from eth2spec.test.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 -@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, + switches_to_compounding=effective + ) - yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) + 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_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 - 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 + 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_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.ETH1_ADDRESS_WITHDRAWAL_PREFIX + spec.COMPOUNDING_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, + withdrawal_credentials=withdrawal_credentials ) 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) +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 = ( - b'\xFF' # Non specified withdrawal credentials version - + b'\x02' * 31 # Garabage bytes + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address ) - amount = spec.MAX_EFFECTIVE_BALANCE + + 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, - withdrawal_credentials=withdrawal_credentials, signed=True, + withdrawal_credentials=withdrawal_credentials ) 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): +def test_process_deposit_request_invalid_sig(spec, state): # fresh deposit = next validator index = validator appended to registry validator_index = len(state.validators) - amount = spec.MAX_EFFECTIVE_BALANCE + # 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, effective=False) + + yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) @with_electra_and_later @spec_state_test -def test_top_up__max_effective_balance(spec, state): +@always_bls +def test_process_deposit_request_top_up_invalid_sig(spec, state): validator_index = 0 - amount = spec.MAX_EFFECTIVE_BALANCE // 4 - deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) + amount = spec.MIN_ACTIVATION_BALANCE // 4 + deposit_request = prepare_deposit_request(spec, validator_index, amount) - state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE - state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE + 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) - 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) +def test_process_deposit_request_set_start_index(spec, state): + assert state.deposit_requests_start_index == spec.UNSET_DEPOSIT_REQUESTS_START_INDEX - 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 + # 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) - 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 + assert state.deposit_requests_start_index == deposit_request.index @with_electra_and_later @spec_state_test -def test_top_up__zero_balance(spec, state): - validator_index = 0 - amount = spec.MAX_EFFECTIVE_BALANCE // 4 +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) - initial_balance = 0 - initial_effective_balance = 0 - state.balances[validator_index] = initial_balance - state.validators[validator_index].effective_balance = initial_effective_balance + state.deposit_requests_start_index = initial_start_index 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 + assert state.deposit_requests_start_index == initial_start_index @with_electra_and_later @spec_state_test -@always_bls -def test_incorrect_sig_top_up(spec, state): +def test_process_deposit_request_switch_to_compounding_normal(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) + 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_switching_to_compounding( + spec, + state, + validator_index, + initial_withdrawal_credentials, + compounding_credentials, + effective=True + ) @with_electra_and_later @spec_state_test -def test_incorrect_withdrawal_credentials_top_up(spec, state): +def test_process_deposit_request_switch_to_compounding_with_excess(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( + # 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, - amount, - withdrawal_credentials=withdrawal_credentials + initial_withdrawal_credentials, + compounding_credentials, + effective=True ) - # 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) +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_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) + 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_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) +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_processing(spec, state, deposit_request, validator_index) - yield from run_process_pending_deposits(spec, state) + 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 -@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 +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_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=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 - - validator = state.validators[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 +@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 + ) + + # Initiate exit + spec.initiate_validator_exit(state, validator_index) + + yield from _run_deposit_request_switching_to_compounding( + spec, + state, + validator_index, + initial_withdrawal_credentials, + compounding_credentials, + effective=True + ) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index 942fe9ab72..d2f15b0d97 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -1,264 +1,467 @@ -from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with from eth2spec.test.context import ( spec_state_test, with_electra_and_later, + always_bls, +) +from tests.core.pyspec.eth2spec.test.helpers.deposits import ( + prepare_pending_deposit, + run_pending_deposit_applying, ) -from eth2spec.test.helpers.keys import privkeys, pubkeys -from tests.core.pyspec.eth2spec.test.helpers.deposits import build_deposit_data from eth2spec.test.helpers.state import next_epoch_via_block +from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable -def run_process_pending_deposits(spec, state): - yield from run_epoch_processing_with( - spec, state, 'process_pending_deposits') +@with_electra_and_later +@spec_state_test +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_add_validator_to_registry(spec, state): +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) - # select validator set outside of the mainnet preset of 256 - index = len(state.validators) + 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.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkeys[index])[1:] + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address ) - deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - withdrawal_credentials, - signed=True) - deposit = spec.PendingDeposit( - pubkey=pubkeys[index], + amount = spec.MIN_ACTIVATION_BALANCE + pending_deposit = prepare_pending_deposit( + spec, + validator_index, + amount, withdrawal_credentials=withdrawal_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - signature=deposit_data.signature, + signed=True, ) - 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 + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) @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 +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, ) - # run test - spec.apply_pending_deposit(state, deposit) - assert state.balances[0] == amount + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_switch_to_compounding(spec, state): - amount = 100 - - # choose a value public key that's in the validator set - index = 0 +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.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - compounding_credentials = ( - spec.COMPOUNDING_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address ) - # 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 - 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, + # 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, ) - 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 + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) @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 +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.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address ) - # wrong compounding - compounding_credentials = ( - spec.hash(b"wrong compounding 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, ) - # 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 - 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, + + 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, ) - 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 + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_switch_to_compounding_has_bls(spec, state): - amount = 100 - - # choose a value public key that's in the validator set - index = 0 - compounding_credentials = ( - spec.COMPOUNDING_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] +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 ) - # 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, + # 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, ) - 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 + + 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 -def test_apply_pending_deposit_switch_to_compounding_invalid_sig(spec, state): - amount = 100 +@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) - # choose a value public key that's in the validator set - index = 0 + 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.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] + spec.COMPOUNDING_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address ) - compounding_credentials = ( - spec.COMPOUNDING_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] + 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 ) - # 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, + 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 ) - 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 + + # 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_switch_to_compounding_not_exited(spec, state): - amount = 100 +def test_apply_pending_deposit_key_validate_invalid_subgroup(spec, state): + validator_index = len(state.validators) + amount = spec.MIN_ACTIVATION_BALANCE - # 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:] + # 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_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 +@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 ) - 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_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 ) - 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, + + 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 ) - 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 + + 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) + assert state.balances[validator_index] == 0 + assert state.validators[validator_index].effective_balance > 0 + next_epoch_via_block(spec, state) + assert state.validators[validator_index].effective_balance == 0 + + # Make a top-up balance to validator + amount = spec.MIN_ACTIVATION_BALANCE // 4 + pending_deposit = prepare_pending_deposit(spec, validator_index, amount, signed=True) + + yield from run_pending_deposit_applying(spec, state, pending_deposit, validator_index) + + 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) + + assert spec.is_fully_withdrawable_validator(validator, balance, current_epoch) diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index 72311be22f..0137f96be0 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -4,6 +4,7 @@ from eth2spec.test.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): - 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 + # 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: - # 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 state.balances[validator_index] == pre_balance - 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 + 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_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:] +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) - 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) + # ensure the transition from eth1 bridge is complete + state.deposit_requests_start_index = state.eth1_deposit_index - yield from run_deposit_request_processing( - spec, - state, - deposit_request, - validator_index, - valid=valid, - effective=effective - ) + # 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 + + assert len(state.pending_deposits) == 0 diff --git a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py index ff0ef2a55d..382c49f51f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py +++ b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py @@ -22,7 +22,7 @@ def get_process_calls(spec): 'charge_confirmed_header_fees', # sharding 'reset_pending_headers', # sharding 'process_eth1_data_reset', - 'process_pending_balance_deposits', # electra + 'process_pending_deposits', # electra 'process_pending_consolidations', # electra 'process_effective_balance_updates', 'process_slashings_reset', @@ -38,8 +38,6 @@ def get_process_calls(spec): 'process_sync_committee_updates', # altair 'process_full_withdrawals', # capella 'process_partial_withdrawals', # capella - 'process_pending_deposits', # electra - 'process_pending_consolidations', # electra # TODO: add sharding processing functions when spec stabilizes. ] diff --git a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_effective_balance_updates.py b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_effective_balance_updates.py index 3fffcb0369..50209e2e7c 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_effective_balance_updates.py +++ b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_effective_balance_updates.py @@ -41,6 +41,20 @@ def run_test_effective_balance_hysteresis(spec, state, with_compounding_credenti (min, min + (hys_inc * div * 2), min + (2 * inc), "exact two step balance increment"), (min, min + (hys_inc * div * 2) + 1, min + (2 * inc), "over two steps, round down"), ] + + if with_compounding_credentials: + min = spec.MIN_ACTIVATION_BALANCE + cases = cases + [ + (min, min + (hys_inc * up), min, "bigger balance, but not high enough"), + (min, min + (hys_inc * up) + 1, min + inc, "bigger balance, high enough, but small step"), + (min, min + (hys_inc * div * 2) - 1, min + inc, "bigger balance, high enough, close to double step"), + (min, min + (hys_inc * div * 2), min + (2 * inc), "exact two step balance increment"), + (min, min + (hys_inc * div * 2) + 1, min + (2 * inc), "over two steps, round down"), + (min, min * 2 + 1, min * 2, "top up or consolidation doubling the balance"), + (min, min * 2 - 1, min * 2 - spec.EFFECTIVE_BALANCE_INCREMENT, + "top up or consolidation almost doubling the balance"), + ] + current_epoch = spec.get_current_epoch(state) for i, (pre_eff, bal, _, _) in enumerate(cases): assert spec.is_active_validator(state.validators[i], current_epoch) From c183be491451cac6541d130402f46c20cee1e2c7 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 26 Aug 2024 16:33:46 +0600 Subject: [PATCH 044/103] Update consolidation tests with pending deposit --- .../test_process_pending_consolidations.py | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py index d750149839..9a3e59bec8 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py @@ -236,10 +236,15 @@ def test_pending_consolidation_future_epoch(spec, state): # Pending balance deposit to the target is created as part of `switch_to_compounding_validator`. # The excess balance to queue are the rewards accumulated over the previous epoch transitions. expected_pending_balance = state_before_consolidation.balances[target_index] - spec.MIN_ACTIVATION_BALANCE - assert len(state.pending_balance_deposits) > 0 - pending_balance_deposit = state.pending_balance_deposits[len(state.pending_balance_deposits) - 1] - assert pending_balance_deposit.index == target_index - assert pending_balance_deposit.amount == expected_pending_balance + target = state.validators[target_index] + pending_deposit = spec.PendingDeposit( + pubkey=target.pubkey, + withdrawal_credentials=target.withdrawal_credentials, + amount=expected_pending_balance, + signature=spec.bls.G2_POINT_AT_INFINITY, + slot=spec.GENESIS_SLOT, + ) + assert state.pending_deposits == [pending_deposit] @with_electra_and_later @@ -290,7 +295,7 @@ def test_pending_consolidation_compounding_creds(spec, state): # Pending balance deposit to the target is not created, # because the target already has compounding credentials - assert len(state.pending_balance_deposits) == 0 + assert len(state.pending_deposits) == 0 @with_electra_and_later @@ -302,15 +307,21 @@ def test_pending_consolidation_with_pending_deposit(spec, state): # initiate source exit spec.initiate_validator_exit(state, source_index) # set withdrawable_epoch to exit_epoch + 1 - state.validators[source_index].withdrawable_epoch = state.validators[source_index].exit_epoch + spec.Epoch(1) + source = state.validators[source_index] + source.withdrawable_epoch = source.exit_epoch + spec.Epoch(1) # append pending consolidation state.pending_consolidations.append( spec.PendingConsolidation(source_index=source_index, target_index=target_index) ) # append pending deposit - state.pending_balance_deposits.append( - spec.PendingBalanceDeposit(index=source_index, amount=spec.MIN_ACTIVATION_BALANCE) + pending_deposit = spec.PendingDeposit( + pubkey=source.pubkey, + withdrawal_credentials=source.withdrawal_credentials, + amount=spec.MIN_ACTIVATION_BALANCE, + signature=spec.bls.G2_POINT_AT_INFINITY, + slot=spec.GENESIS_SLOT, ) + state.pending_deposits.append(pending_deposit) # Set the source and the target withdrawal credential to compounding state.validators[source_index].withdrawal_credentials = ( spec.COMPOUNDING_WITHDRAWAL_PREFIX + b"\x00" * 11 + b"\x11" * 20 @@ -320,12 +331,12 @@ def test_pending_consolidation_with_pending_deposit(spec, state): ) # Advance to withdrawable_epoch - 1 with full participation - target_epoch = state.validators[source_index].withdrawable_epoch - spec.Epoch(1) + target_epoch = source.withdrawable_epoch - spec.Epoch(1) while spec.get_current_epoch(state) < target_epoch: next_epoch_with_full_participation(spec, state) - # Obtain state before the call to process_pending_balance_deposits - state_before_consolidation = compute_state_by_epoch_processing_to(spec, state, "process_pending_balance_deposits") + # Obtain state before the call to process_pending_deposits + state_before_consolidation = compute_state_by_epoch_processing_to(spec, state, "process_pending_deposits") yield from run_epoch_processing_with(spec, state, "process_pending_consolidations") @@ -341,8 +352,6 @@ def test_pending_consolidation_with_pending_deposit(spec, state): assert state.balances[source_index] == 0 assert state.pending_consolidations == [] - # Pending balance deposit to the source was not processed. + # Pending deposit to the source was not processed. # It should only be processed in the next epoch transition - assert len(state.pending_balance_deposits) == 1 - assert state.pending_balance_deposits[0] == spec.PendingBalanceDeposit( - index=source_index, amount=spec.MIN_ACTIVATION_BALANCE) + assert state.pending_deposits == [pending_deposit] From 7afe8e07425190923c3201a124450b492b306006 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 26 Aug 2024 17:00:45 +0600 Subject: [PATCH 045/103] Update eip7732 with pending_deposits --- specs/_features/eip7732/beacon-chain.md | 2 +- specs/_features/eip7732/fork.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/_features/eip7732/beacon-chain.md b/specs/_features/eip7732/beacon-chain.md index fdcb2b3008..58a2c788a5 100644 --- a/specs/_features/eip7732/beacon-chain.md +++ b/specs/_features/eip7732/beacon-chain.md @@ -267,7 +267,7 @@ class BeaconState(Container): earliest_exit_epoch: Epoch consolidation_balance_to_consume: Gwei earliest_consolidation_epoch: Epoch - pending_balance_deposits: List[PendingBalanceDeposit, PENDING_BALANCE_DEPOSITS_LIMIT] + pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT] pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT] pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] # PBS diff --git a/specs/_features/eip7732/fork.md b/specs/_features/eip7732/fork.md index 942b9c30cc..120e64fa46 100644 --- a/specs/_features/eip7732/fork.md +++ b/specs/_features/eip7732/fork.md @@ -126,7 +126,7 @@ def upgrade_to_eip7732(pre: electra.BeaconState) -> BeaconState: earliest_exit_epoch=pre.earliest_exit_epoch, consolidation_balance_to_consume=pre.consolidation_balance_to_consume, earliest_consolidation_epoch=pre.earliest_consolidation_epoch, - pending_balance_deposits=pre.pending_balance_deposits, + pending_deposits=pre.pending_deposits, pending_partial_withdrawals=pre.pending_partial_withdrawals, pending_consolidations=pre.pending_consolidations, # ePBS From ad8f54ff7db197160449cb4c9271ca5ebaaa689d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 27 Aug 2024 17:26:54 +0600 Subject: [PATCH 046/103] Update pending deposits tests --- .../test_process_deposit_request.py | 4 +- .../test_apply_pending_deposit.py | 26 +- .../test_process_pending_deposits.py | 802 ++++-------------- .../pyspec/eth2spec/test/helpers/deposits.py | 91 +- .../pyspec/eth2spec/test/helpers/state.py | 5 + 5 files changed, 207 insertions(+), 721 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py index 1e78129034..220abc80fa 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py @@ -189,7 +189,7 @@ def test_process_deposit_request_switch_to_compounding_normal(spec, state): + b'\x00' * 11 # specified 0s + b'\x59' * 20 # a 20-byte eth1 address ) - + yield from _run_deposit_request_switching_to_compounding( spec, state, @@ -219,7 +219,7 @@ def test_process_deposit_request_switch_to_compounding_with_excess(spec, state): ) 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, diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py index d2f15b0d97..cc3c2205a7 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py @@ -334,6 +334,7 @@ def test_apply_pending_deposit_incorrect_withdrawal_credentials_top_up(spec, sta @with_electra_and_later @spec_state_test +@always_bls def test_apply_pending_deposit_key_validate_invalid_subgroup(spec, state): validator_index = len(state.validators) amount = spec.MIN_ACTIVATION_BALANCE @@ -348,6 +349,7 @@ def test_apply_pending_deposit_key_validate_invalid_subgroup(spec, state): @with_electra_and_later @spec_state_test +@always_bls def test_apply_pending_deposit_key_validate_invalid_decompression(spec, state): validator_index = len(state.validators) amount = spec.MIN_ACTIVATION_BALANCE @@ -366,13 +368,13 @@ def test_apply_pending_deposit_key_validate_invalid_decompression(spec, state): @spec_state_test @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') + 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, + fork_version=fork_version, signed=True ) @@ -388,13 +390,13 @@ def test_apply_pending_deposit_with_previous_fork_version(spec, state): # 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 + 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, + fork_version=fork_version, signed=True ) @@ -405,13 +407,13 @@ def test_apply_pending_deposit_with_previous_fork_version(spec, state): @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 + 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, + fork_version=fork_version, signed=True ) @@ -424,13 +426,13 @@ def test_ineffective_deposit_with_current_fork_version(spec, state): 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 + validator_index = len(state.validators) + fork_version = spec.config.GENESIS_FORK_VERSION pending_deposit = prepare_pending_deposit( spec, validator_index=validator_index, amount=spec.MIN_ACTIVATION_BALANCE, - fork_version=spec.config.GENESIS_FORK_VERSION, + fork_version=fork_version, signed=True ) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index 8f27c7db10..cc78e39d50 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -1,16 +1,14 @@ from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with from eth2spec.test.context import ( spec_state_test, - always_bls, with_electra_and_later, ) -from eth2spec.test.helpers.deposits import ( - build_pending_deposit, - build_pending_deposit_top_up, +from eth2spec.test.helpers.deposits import prepare_pending_deposit +from eth2spec.test.helpers.state import ( + next_epoch_with_full_participation, + advance_finality_to, + set_full_participation, ) -from eth2spec.test.helpers.state import next_epoch_via_block - -from eth2spec.test.helpers.keys import pubkeys def run_process_pending_deposits(spec, state): @@ -18,397 +16,214 @@ def run_process_pending_deposits(spec, state): spec, state, 'process_pending_deposits') -@with_electra_and_later -@spec_state_test -def test_pending_deposit_under_max(spec, state): - # pick an amount that adds to less than churn limit - amount = 100 - undermax = spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING - 1 - for i in range(undermax): - print(i) - state.pending_deposits.append( - spec.PendingDeposit( - pubkey=state.validators[i].pubkey, - withdrawal_credentials=( - state.validators[i].withdrawal_credentials - ), - amount=amount, - slot=spec.GENESIS_SLOT, - ) - ) - assert len(state.pending_deposits) == undermax, \ - "pending deposits is not undermax" - yield from run_process_pending_deposits(spec, state) - assert len(state.pending_deposits) == 0 - assert state.balances[0] == amount + spec.MIN_ACTIVATION_BALANCE - assert state.balances[undermax - 1] == amount + spec.MIN_ACTIVATION_BALANCE - assert state.balances[undermax] == spec.MIN_ACTIVATION_BALANCE +def _ensure_enough_churn_to_process_deposits(spec, state): + state.deposit_balance_to_consume = sum(d.amount for d in state.pending_deposits) -@with_electra_and_later -@spec_state_test -def test_pending_deposit_max(spec, state): - # pick an amount that adds to less than churn limit - amount = 100 - max = spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING - for i in range(max): - state.pending_deposits.append( - spec.PendingDeposit( - pubkey=state.validators[i].pubkey, - withdrawal_credentials=( - state.validators[i].withdrawal_credentials - ), - amount=amount, - slot=spec.GENESIS_SLOT, - ) +def _prepare_eth1_bridge_deprecation(spec, state, eth1_bridge_flags): + new_pending_deposits = [] + validator_index_base = len(state.validators) + deposit_request_slot = spec.Slot(1) + for index, eth1_bridge in enumerate(eth1_bridge_flags): + validator_index = validator_index_base + index + slot = spec.GENESIS_SLOT if eth1_bridge else deposit_request_slot + pending_deposit = prepare_pending_deposit( + spec, + validator_index=validator_index, + amount=spec.MIN_ACTIVATION_BALANCE, + signed=True, + slot=slot, ) - assert len(state.pending_deposits) == max, \ - "pending deposits is not max" - yield from run_process_pending_deposits(spec, state) - assert len(state.pending_deposits) == 0 - assert state.balances[0] == amount + spec.MIN_ACTIVATION_BALANCE - assert state.balances[max - 1] == amount + spec.MIN_ACTIVATION_BALANCE - assert state.balances[max] == spec.MIN_ACTIVATION_BALANCE + new_pending_deposits.append(pending_deposit) + + # Eth1 bridge deposits instantly yield new validator records + if eth1_bridge: + spec.add_validator_to_registry( + state, + pending_deposit.pubkey, + pending_deposit.withdrawal_credentials, + spec.Gwei(0) + ) + state.eth1_deposit_index += 1 + # Advance state to make pending deposits finalized + advance_finality_to(spec, state, spec.compute_epoch_at_slot(deposit_request_slot) + 1) -@with_electra_and_later -@spec_state_test -def test_pending_deposit_over_max(spec, state): - # pick an amount that adds to less than churn limit - amount = 100 - overmax = spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING + 1 - for i in range(overmax): - state.pending_deposits.append( - spec.PendingDeposit( - pubkey=state.validators[i].pubkey, - withdrawal_credentials=( - state.validators[i].withdrawal_credentials - ), - amount=amount, - slot=spec.GENESIS_SLOT, - ) - ) - assert len(state.pending_deposits) == overmax, \ - "pending deposits is not overmax" - yield from run_process_pending_deposits(spec, state) - assert len(state.pending_deposits) == 1 - assert state.balances[overmax - 1] == spec.MIN_ACTIVATION_BALANCE + amount + # Add pending deposits + for pending_deposit in new_pending_deposits: + state.pending_deposits.append(pending_deposit) + # Ensure there is enough churn to process them all + _ensure_enough_churn_to_process_deposits(spec, state) -@with_electra_and_later -@spec_state_test -def test_new_deposit_eth1_withdrawal_credentials(spec, state): - state.validators[0].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 + return state, new_pending_deposits - state.pending_deposits.append(spec.PendingDeposit( - pubkey=state.validators[0].pubkey, - withdrawal_credentials=state.validators[0].withdrawal_credentials, - amount=amount, - slot=1, - )) - yield from run_process_pending_deposits(spec, state) +def _check_pending_deposits_induced_new_validators(spec, state, pre_validator_count, applied_pending_deposits): + assert pre_validator_count + len(applied_pending_deposits) == len(state.validators) + eth1_bridge_deposits = [d for d in applied_pending_deposits if d.slot == spec.GENESIS_SLOT] + deposit_requests = [d for d in applied_pending_deposits if d.slot > spec.GENESIS_SLOT] -@with_electra_and_later -@spec_state_test -def test_new_deposit_non_versioned_withdrawal_credentials(spec, state): - state.validators[0].withdrawal_credentials = ( - b'\xFF' # Non specified withdrawal credentials version - + b'\x02' * 31 # Garabage bytes - ) - amount = spec.MAX_EFFECTIVE_BALANCE - state.pending_deposits.append(spec.PendingDeposit( - pubkey=state.validators[0].pubkey, - withdrawal_credentials=state.validators[0].withdrawal_credentials, - amount=amount, - slot=1, - )) + # Eth1 bridge deposits should induce new validators in the first place + for index, deposit in enumerate(eth1_bridge_deposits): + validator_index = pre_validator_count + index + validator = state.validators[validator_index] + assert state.balances[validator_index] == deposit.amount + assert validator.pubkey == deposit.pubkey + # Effective balance is updated after pending deposits by process_effective_balance_updates + assert validator.effective_balance == spec.Gwei(0) + assert validator.withdrawal_credentials == deposit.withdrawal_credentials - yield from run_process_pending_deposits(spec, state) + # then deposit requests go + for index, deposit in enumerate(deposit_requests): + validator_index = pre_validator_count + len(eth1_bridge_deposits) + index + validator = state.validators[validator_index] + assert state.balances[validator_index] == deposit.amount + assert validator.pubkey == deposit.pubkey + assert validator.withdrawal_credentials == deposit.withdrawal_credentials + # Validators induced from deposit requests get instant update of the EB + assert validator.effective_balance == deposit.amount @with_electra_and_later @spec_state_test -def test_pending_deposit_eth1_bridge_pending(spec, state): - amount = spec.MIN_ACTIVATION_BALANCE +def test_process_pending_deposits_eth1_bridge_transition_pending(spec, state): # There are pending Eth1 bridge deposits # state.eth1_deposit_index < state.deposit_requests_start_index - state.deposit_requests_start_index = state.eth1_deposit_index + 1 - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - pd = build_pending_deposit(spec, index, - amount=amount, - withdrawal_credentials=withdrawal_credentials, - signed=True, - slot=1) - state.pending_deposits.append(pd) - # set deposit_balance_to_consume to some initial amount - state.deposit_balance_to_consume = amount - yield from run_process_pending_deposits(spec, state) - # deposit_balance_to_consume was reset to 0 - assert state.deposit_balance_to_consume == 0 - # deposit was postponed and not processed - assert len(state.pending_deposits) == 1 + pre_validator_count = len(state.validators) + state.eth1_data.deposit_count = len(state.validators) + 3 + state.deposit_requests_start_index = state.eth1_data.deposit_count + state, new_pending_deposits = _prepare_eth1_bridge_deprecation(spec, state, [True, True, False]) + assert state.eth1_deposit_index < state.deposit_requests_start_index -@with_electra_and_later -@spec_state_test -def test_pending_deposit_eth1_bridge_not_applied(spec, state): - amount = spec.MIN_ACTIVATION_BALANCE - # deposit is not finalized yet, so it is postponed - state.pending_deposits.append(spec.PendingDeposit( - pubkey=state.validators[0].pubkey, - withdrawal_credentials=state.validators[0].withdrawal_credentials, - amount=amount, - slot=1, - )) - # set deposit_requests_start_index to something high - state.deposit_requests_start_index = 100000000000000000 - # set deposit_balance_to_consume to some initial amount - state.deposit_balance_to_consume = amount yield from run_process_pending_deposits(spec, state) + + # Eth1 bridge deposits were applied + _check_pending_deposits_induced_new_validators(spec, state, pre_validator_count, new_pending_deposits[:2]) + # deposit request was postponed and not processed + assert state.pending_deposits == new_pending_deposits[2:] # deposit_balance_to_consume was reset to 0 assert state.deposit_balance_to_consume == 0 - # deposit was postponed and not processed - assert len(state.pending_deposits) == 1 @with_electra_and_later @spec_state_test -def test_pending_deposit_no_eth1_bridge_pending(spec, state): - amount = spec.MIN_ACTIVATION_BALANCE - # there is no pending eth1 bridge deposits - # state.eth1_deposit_index == state.deposit_requests_start_index - state.deposit_requests_start_index = state.eth1_deposit_index - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - pd = build_pending_deposit(spec, index, - amount=amount, - withdrawal_credentials=withdrawal_credentials, - signed=True) - state.pending_deposits.append(pd) - # set deposit_balance_to_consume to some initial amount - state.deposit_balance_to_consume = amount - yield from run_process_pending_deposits(spec, state) - # deposit_balance_to_consume was reset to 0 - assert state.deposit_balance_to_consume == 0 - # all pending deposits processed - assert len(state.pending_deposits) == 0 +def test_process_pending_deposits_eth1_bridge_transition_not_applied(spec, state): + # There are pending Eth1 bridge deposits + # state.eth1_deposit_index < state.deposit_requests_start_index + pre_validator_count = len(state.validators) + state.eth1_data.deposit_count = len(state.validators) + 3 + state.deposit_requests_start_index = state.eth1_data.deposit_count + state, new_pending_deposits = _prepare_eth1_bridge_deprecation(spec, state, [False, True, True]) + assert state.eth1_deposit_index < state.deposit_requests_start_index -@with_electra_and_later -@spec_state_test -def test_pending_deposit_not_finalized(spec, state): - amount = spec.MIN_ACTIVATION_BALANCE - # set slot to something not finalized - # deposit is not finalized yet, so it is postponed - state.pending_deposits.append(spec.PendingDeposit( - pubkey=state.validators[0].pubkey, - withdrawal_credentials=state.validators[0].withdrawal_credentials, - amount=amount, - slot=spec.get_current_epoch(state) + 1, - )) - # skip the bridge validation - state.deposit_requests_start_index = 0 - # set deposit_balance_to_consume to some initial amount - state.deposit_balance_to_consume = amount yield from run_process_pending_deposits(spec, state) + + # no pending deposit was processed, however Eth1 bridge deposits induced new validators + assert pre_validator_count + 2 == len(state.validators) + assert state.pending_deposits == new_pending_deposits # deposit_balance_to_consume was reset to 0 assert state.deposit_balance_to_consume == 0 - # deposit was postponed and not processed - assert len(state.pending_deposits) == 1 @with_electra_and_later @spec_state_test -def test_process_pending_deposits_limit_is_reached(spec, state): - amount = 5000 - cumulative_amount = 0 - # set pending deposits to the maximum - for i in range(spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING+1): - wc = state.validators[i].withdrawal_credentials - pd = build_pending_deposit(spec, i, - amount=amount, - withdrawal_credentials=wc, - signed=True) - state.pending_deposits.append(pd) - cumulative_amount += amount - # churn limit was not reached - assert cumulative_amount < spec.get_activation_exit_churn_limit(state) - yield from run_process_pending_deposits(spec, state) - # no deposits above limit were processed - assert len(state.pending_deposits) == 1 +def test_process_pending_deposits_eth1_bridge_transition_complete(spec, state): + # There is no pending Eth1 bridge deposits + # state.eth1_deposit_index == state.deposit_requests_start_index + pre_validator_count = len(state.validators) + state.eth1_data.deposit_count = len(state.validators) + 2 + state.deposit_requests_start_index = state.eth1_data.deposit_count + state, new_pending_deposits = _prepare_eth1_bridge_deprecation(spec, state, [True, False, True]) + assert state.eth1_deposit_index == state.deposit_requests_start_index -@with_electra_and_later -@spec_state_test -def test_pending_deposit_validator_withdrawn(spec, state): - amount = spec.MIN_ACTIVATION_BALANCE - 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 - previous_epoch = spec.get_current_epoch(state) - 1 - # set validator to be exited by current epoch - state.validators[index].exit_epoch = previous_epoch - # set validator to be withdrawable by current epoch - state.validators[index].withdrawable_epoch = previous_epoch - # set withdrawal credentials to compounding but should not switch since - # validator is already withdrawing - pd = build_pending_deposit(spec, index, - amount=amount, - withdrawal_credentials=compounding_credentials, - signed=True) - state.pending_deposits.append(pd) - # skip the bridge validation - state.deposit_requests_start_index = 0 - # set deposit_balance_to_consume to some initial amount - state.deposit_balance_to_consume = amount - # reset balance for assert - state.balances[0] = 0 - old_validator_count = len(state.validators) yield from run_process_pending_deposits(spec, state) - btc = state.deposit_balance_to_consume - # deposit_balance_to_consume was reset to 0 - assert btc == 0 - # deposit was processed - assert state.pending_deposits == [] - # balance increases because of withdraw - assert state.balances[0] == amount - # churn limit was not reached - assert not amount > spec.get_activation_exit_churn_limit(state) - # validator count should stay the same - assert len(state.validators) == old_validator_count - -@with_electra_and_later -@spec_state_test -def test_pending_deposit_validator_exiting_but_not_withdrawn(spec, state): - amount = spec.MIN_ACTIVATION_BALANCE - hash = spec.hash(state.validators[0].pubkey)[1:] - withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + hash - # advance the state - next_epoch_via_block(spec, state) - state.validators[0].withdrawal_credentials = withdrawal_credentials - # set validator to be withdrawable by current epoch - state.validators[0].exit_epoch = spec.get_current_epoch(state) - 1 - state.validators[0].withdrawable_epoch = spec.FAR_FUTURE_EPOCH - state.pending_deposits.append(spec.PendingDeposit( - pubkey=state.validators[0].pubkey, - withdrawal_credentials=state.validators[0].withdrawal_credentials, - amount=amount, - slot=spec.GENESIS_SLOT, - )) - # skip the bridge validation - state.deposit_requests_start_index = 0 - # set deposit_balance_to_consume to some initial amount - state.deposit_balance_to_consume = amount - - yield from run_process_pending_deposits(spec, state) + # all deposits were applied + assert state.pending_deposits == [] + _check_pending_deposits_induced_new_validators(spec, state, pre_validator_count, new_pending_deposits) # deposit_balance_to_consume was reset to 0 assert state.deposit_balance_to_consume == 0 - # deposit was postponed and not processed - assert len(state.pending_deposits) == 1 @with_electra_and_later @spec_state_test -def test_pending_deposit_not_in_validator_set(spec, state): - index = 2000 - amount = spec.MIN_ACTIVATION_BALANCE - wc = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - pd = build_pending_deposit(spec, index, - amount=amount, - withdrawal_credentials=wc, - signed=True) - - state.pending_deposits.append(pd) - old_length = len(state.validators) - yield from run_process_pending_deposits(spec, state) - # new validator activated - assert len(state.validators) == old_length + 1 +def test_process_pending_deposits_not_finalized(spec, state): + # complete eth1 bridge transition + state.deposit_requests_start_index = 0 + # advance state three epochs into the future + for _ in range(0, 3): + next_epoch_with_full_participation(spec, state) + # create pending deposits + pre_validator_count = len(state.validators) + for index in range(0, 2): + state.pending_deposits.append( + prepare_pending_deposit( + spec, + validator_index=pre_validator_count + index, + amount=spec.MIN_ACTIVATION_BALANCE, + signed=True, + slot=state.slot + index + ) + ) + new_pending_deposits = state.pending_deposits.copy() + # finalize a slot before the slot of the first deposit + advance_finality_to(spec, state, spec.get_current_epoch(state) - 1) -@with_electra_and_later -@spec_state_test -def test_pending_deposit_min_activation_balance(spec, state): - index = 0 - amount = spec.MIN_ACTIVATION_BALANCE - state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, index, amount) - ) - pre_balance = state.balances[index] + # process pending deposits + # the slot of the first deposit will be finalized before the call to process_pending_deposits + set_full_participation(spec, state) + _ensure_enough_churn_to_process_deposits(spec, state) yield from run_process_pending_deposits(spec, state) - assert state.balances[index] == pre_balance + amount - # No leftover deposit balance to consume + # deposit_balance_to_consume was reset to 0 assert state.deposit_balance_to_consume == 0 - assert state.pending_deposits == [] + # second deposit was not processed as it hasn't been finalized + assert state.pending_deposits == new_pending_deposits[1:] + _check_pending_deposits_induced_new_validators(spec, state, pre_validator_count, new_pending_deposits[:1]) @with_electra_and_later @spec_state_test -def test_pending_deposit_balance_equal_churn(spec, state): - index = 0 - amount = spec.get_activation_exit_churn_limit(state) - state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, index, amount) - ) - pre_balance = state.balances[index] +def test_process_pending_deposits_limit_is_reached(spec, state): + # set pending deposits to the maximum + amount = spec.EFFECTIVE_BALANCE_INCREMENT * 1 + for i in range(spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING + 2): + wc = state.validators[i].withdrawal_credentials + pd = prepare_pending_deposit(spec, i, amount, withdrawal_credentials=wc, signed=True) + state.pending_deposits.append(pd) + new_pending_deposits = state.pending_deposits.copy() + + # process pending deposits + pre_balances = state.balances.copy() + _ensure_enough_churn_to_process_deposits(spec, state) yield from run_process_pending_deposits(spec, state) - assert state.balances[index] == pre_balance + amount + # deposit_balance_to_consume was reset to 0 assert state.deposit_balance_to_consume == 0 - assert state.pending_deposits == [] + # no deposits above limit were processed + assert state.pending_deposits == new_pending_deposits[spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING:] + for i in range(spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING): + assert state.balances[i] == pre_balances[i] + amount + for i in range(spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING, spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING + 2): + assert state.balances[i] == pre_balances[i] @with_electra_and_later @spec_state_test -def test_pending_deposit_balance_equal_churn_with_compounding(spec, state): +def test_process_pending_deposits_balance_equal_churn(spec, state): 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:] - ) amount = spec.get_activation_exit_churn_limit(state) - state.validators[index].withdrawal_credentials = withdrawal_credentials - - # set withdrawal credentials to compounding but should not switch since - # validator is already withdrawing - pd = build_pending_deposit(spec, index, - amount=amount, - withdrawal_credentials=compounding_credentials, - signed=True) - state.pending_deposits.append(pd) + state.pending_deposits.append( + prepare_pending_deposit(spec, index, amount) + ) pre_balance = state.balances[index] yield from run_process_pending_deposits(spec, state) @@ -416,83 +231,15 @@ def test_pending_deposit_balance_equal_churn_with_compounding(spec, state): assert state.balances[index] == pre_balance + amount assert state.deposit_balance_to_consume == 0 assert state.pending_deposits == [] - current_credentials = state.validators[0].withdrawal_credentials - # validator is not exited, so it should not switch to compounding - assert current_credentials == withdrawal_credentials - - -@with_electra_and_later -@spec_state_test -def test_top_up__zero_balance(spec, state): - validator_index = 0 - amount = spec.MAX_EFFECTIVE_BALANCE // 4 - - initial_balance = 0 - initial_effective_balance = 0 - state.balances[validator_index] = initial_balance - val = state.validators[validator_index] - val.effective_balance = initial_effective_balance - wc = val.withdrawal_credentials - pd = build_pending_deposit(spec, validator_index, - amount=amount, - withdrawal_credentials=wc, - signed=True) - state.pending_deposits.append(pd) - - yield from run_process_pending_deposits(spec, state) - - deposits_len = len(state.pending_deposits) - assert state.pending_deposits[deposits_len - 1].amount == amount - # unchanged effective balance - assert val.effective_balance == initial_effective_balance @with_electra_and_later @spec_state_test -def test_incorrect_sig_top_up(spec, state): - validator_index = 0 - amount = spec.MAX_EFFECTIVE_BALANCE // 4 - - val = state.validators[validator_index] - wc = val.withdrawal_credentials - pd = build_pending_deposit(spec, validator_index, - amount=amount, - withdrawal_credentials=wc, - signed=False) - state.pending_deposits.append(pd) - - 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 - - initial_balance = 0 - initial_effective_balance = 0 - state.balances[validator_index] = initial_balance - val = state.validators[validator_index] - val.effective_balance = initial_effective_balance - wc = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(b"junk")[1:] - - pd = build_pending_deposit(spec, validator_index, - amount=amount, - withdrawal_credentials=wc, - signed=True) - state.pending_deposits.append(pd) - - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -def test_pending_deposit_balance_above_churn(spec, state): +def test_process_pending_deposits_balance_above_churn(spec, state): index = 0 amount = spec.get_activation_exit_churn_limit(state) + 1 state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, index, amount) + prepare_pending_deposit(spec, index, amount) ) pre_balance = state.balances[index] @@ -505,61 +252,18 @@ def test_pending_deposit_balance_above_churn(spec, state): assert state.deposit_balance_to_consume == wantedBalanceToConsume # deposit is still in the queue assert state.pending_deposits == [ - build_pending_deposit_top_up(spec, state, index, amount) + prepare_pending_deposit(spec, index, amount) ] @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 - wc = state.validators[validator_index].withdrawal_credentials - pd = build_pending_deposit(spec, validator_index, - amount=amount, - withdrawal_credentials=wc, - signed=True) - state.pending_deposits.append(pd) - - state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE - state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE - - yield from run_process_pending_deposits(spec, state) - - 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 - wc = state.validators[validator_index].withdrawal_credentials - pd = build_pending_deposit(spec, validator_index, - amount=amount, - withdrawal_credentials=wc, - signed=True) - state.pending_deposits.append(pd) - - 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_process_pending_deposits(spec, state) - - # unchanged effective balance - assert state.validators[validator_index].effective_balance == initial_effective_balance - - -@with_electra_and_later -@spec_state_test -def test_pending_deposit_preexisting_churn(spec, state): +def test_process_pending_deposits_preexisting_churn(spec, state): index = 0 - amount = 10**9 + 1 + amount = spec.EFFECTIVE_BALANCE_INCREMENT + 1 state.deposit_balance_to_consume = 2 * amount state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, index, amount) + prepare_pending_deposit(spec, index, amount) ) pre_balance = state.balances[index] @@ -575,15 +279,13 @@ def test_pending_deposit_preexisting_churn(spec, state): @with_electra_and_later @spec_state_test -def test_multiple_pending_deposits_below_churn(spec, state): - amount = 10**9 +def test_process_pending_deposits_multiple_pending_deposits_below_churn(spec, state): + amount = spec.EFFECTIVE_BALANCE_INCREMENT state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, - validator_index=0, amount=amount) + prepare_pending_deposit(spec, validator_index=0, amount=amount) ) state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, - validator_index=1, amount=amount) + prepare_pending_deposit(spec, validator_index=1, amount=amount) ) pre_balances = state.balances.copy() @@ -598,13 +300,12 @@ def test_multiple_pending_deposits_below_churn(spec, state): @with_electra_and_later @spec_state_test -def test_multiple_pending_deposits_above_churn(spec, state): +def test_process_pending_deposits_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_deposits.append( - build_pending_deposit_top_up(spec, state, - validator_index=i, amount=amount) + prepare_pending_deposit(spec, validator_index=i, amount=amount) ) pre_balances = state.balances.copy() @@ -621,19 +322,17 @@ def test_multiple_pending_deposits_above_churn(spec, state): ) # third deposit is still in the queue assert state.pending_deposits == [ - build_pending_deposit_top_up(spec, state, - validator_index=2, amount=amount) + prepare_pending_deposit(spec, validator_index=2, amount=amount) ] @with_electra_and_later @spec_state_test -def test_skipped_deposit_exiting_validator(spec, state): +def test_process_pending_deposits_skipped_deposit_exiting_validator(spec, state): index = 0 amount = spec.MIN_ACTIVATION_BALANCE state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, - validator_index=index, amount=amount) + prepare_pending_deposit(spec, validator_index=index, amount=amount) ) pre_pending_deposits = state.pending_deposits.copy() pre_balance = state.balances[index] @@ -652,13 +351,12 @@ def test_skipped_deposit_exiting_validator(spec, state): @with_electra_and_later @spec_state_test -def test_multiple_skipped_deposits_exiting_validators(spec, state): +def test_process_pending_deposits_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_deposits.append( - build_pending_deposit_top_up(spec, state, - validator_index=i, amount=amount) + prepare_pending_deposit(spec, validator_index=i, amount=amount) ) # Initiate the exit of validator i @@ -678,12 +376,11 @@ def test_multiple_skipped_deposits_exiting_validators(spec, state): @with_electra_and_later @spec_state_test -def test_multiple_pending_one_skipped(spec, state): +def test_process_pending_deposits_multiple_pending_one_skipped(spec, state): amount = spec.EFFECTIVE_BALANCE_INCREMENT for i in [0, 1, 2]: state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, - validator_index=i, amount=amount) + prepare_pending_deposit(spec, validator_index=i, amount=amount) ) pre_balances = state.balances.copy() # Initiate the second validator's exit @@ -699,27 +396,22 @@ def test_multiple_pending_one_skipped(spec, state): assert state.deposit_balance_to_consume == 0 # second deposit is still in the queue assert state.pending_deposits == [ - build_pending_deposit_top_up(spec, state, - validator_index=1, amount=amount) + prepare_pending_deposit(spec, validator_index=1, amount=amount) ] @with_electra_and_later @spec_state_test -def test_mixture_of_skipped_and_above_churn(spec, state): +def test_process_pending_deposits_mixture_of_skipped_and_above_churn(spec, state): amount01 = spec.EFFECTIVE_BALANCE_INCREMENT amount2 = spec.MAX_EFFECTIVE_BALANCE_ELECTRA # First two validators have small deposit, third validators a large one for i in [0, 1]: state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, - validator_index=i, - amount=amount01) + prepare_pending_deposit(spec, validator_index=i, amount=amount01) ) state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, - validator_index=2, - amount=amount2) + prepare_pending_deposit(spec, validator_index=2, amount=amount2) ) pre_balances = state.balances.copy() # Initiate the second validator's exit @@ -738,22 +430,18 @@ def test_mixture_of_skipped_and_above_churn(spec, state): assert state.deposit_balance_to_consume == wanted_balance # second and third deposit still in the queue 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) + prepare_pending_deposit(spec, validator_index=2, amount=amount2), + prepare_pending_deposit(spec, validator_index=1, amount=amount01) ] @with_electra_and_later @spec_state_test -def test_processing_deposit_of_withdrawable_validator(spec, state): +def test_process_pending_deposits_withdrawable_validator(spec, state): index = 0 amount = spec.MIN_ACTIVATION_BALANCE state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, - validator_index=index, - amount=amount) + prepare_pending_deposit(spec, validator_index=index, amount=amount) ) pre_balance = state.balances[index] # Initiate the validator's exit @@ -773,12 +461,11 @@ def test_processing_deposit_of_withdrawable_validator(spec, state): @with_electra_and_later @spec_state_test -def test_processing_deposit_of_withdrawable_validator_not_churned(spec, state): +def test_process_pending_deposits_withdrawable_validator_not_churned(spec, state): amount = spec.MAX_EFFECTIVE_BALANCE_ELECTRA for i in [0, 1]: state.pending_deposits.append( - build_pending_deposit_top_up(spec, state, - validator_index=i, amount=amount) + prepare_pending_deposit(spec, validator_index=i, amount=amount) ) pre_balances = state.balances.copy() # Initiate the first validator's exit @@ -799,140 +486,5 @@ def test_processing_deposit_of_withdrawable_validator_not_churned(spec, state): wanted_limit = spec.get_activation_exit_churn_limit(state) assert state.deposit_balance_to_consume == wanted_limit assert state.pending_deposits == [ - build_pending_deposit_top_up(spec, state, - validator_index=1, amount=amount) + prepare_pending_deposit(spec, validator_index=1, amount=amount) ] - - -@with_electra_and_later -@spec_state_test -@always_bls -def test_correct_sig_but_forked_state(spec, state): - amount = spec.MAX_EFFECTIVE_BALANCE - # deposits will always be valid, regardless of the current fork - state.fork.current_version = spec.Version('0x1234abcd') - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - wc = withdrawal_credentials - pd = build_pending_deposit(spec, index, - amount=amount, - withdrawal_credentials=wc, - signed=True) - state.pending_deposits.append(pd) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -def test_key_validate_invalid_subgroup(spec, state): - amount = spec.MAX_EFFECTIVE_BALANCE - - # All-zero pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception. - pubkey = b'\x00' * 48 - - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkey)[1:] - ) - wc = withdrawal_credentials - pd = build_pending_deposit(spec, index, - amount=amount, - withdrawal_credentials=wc, - pubkey=pubkey, - signed=True) - state.pending_deposits.append(pd) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -def test_key_validate_invalid_decompression(spec, state): - 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) - - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkey)[1:] - ) - wc = withdrawal_credentials - pd = build_pending_deposit(spec, index, - amount=amount, - withdrawal_credentials=wc, - pubkey=pubkey, - signed=True) - state.pending_deposits.append(pd) - yield from run_process_pending_deposits(spec, state) - - -@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`. - assert state.fork.previous_version != state.fork.current_version - amount = spec.MAX_EFFECTIVE_BALANCE - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - wc = withdrawal_credentials - pd = build_pending_deposit(spec, index, - amount=amount, - withdrawal_credentials=wc, - fork_version=state.fork.previous_version, - signed=True) - state.pending_deposits.append(pd) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -@always_bls -def test_apply_pending_deposit_with_genesis_fork_version(spec, state): - assert spec.config.GENESIS_FORK_VERSION not in (state.fork.previous_version, state.fork.current_version) - - assert state.fork.previous_version != state.fork.current_version - amount = spec.MAX_EFFECTIVE_BALANCE - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - wc = withdrawal_credentials - pd = build_pending_deposit(spec, index, - amount=amount, - withdrawal_credentials=wc, - fork_version=spec.GENESIS_FORK_VERSION, - signed=True) - state.pending_deposits.append(pd) - yield from run_process_pending_deposits(spec, state) - - -@with_electra_and_later -@spec_state_test -@always_bls -def test_apply_pending_deposit_with_bad_fork_version(spec, state): - amount = spec.MAX_EFFECTIVE_BALANCE - index = 0 - withdrawal_credentials = ( - spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + - spec.hash(pubkeys[index])[1:] - ) - wc = withdrawal_credentials - pd = build_pending_deposit(spec, index, - amount=amount, - withdrawal_credentials=wc, - fork_version=spec.Version('0xAaBbCcDd'), - signed=True) - state.pending_deposits.append(pd) - yield from run_process_pending_deposits(spec, state) diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index 0137f96be0..f045d1fcda 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -35,7 +35,7 @@ def build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, fo return deposit_data -def sign_deposit_data(spec, deposit_data, privkey, fork_version): +def sign_deposit_data(spec, deposit_data, privkey, fork_version=None): deposit_message = spec.DepositMessage( pubkey=deposit_data.pubkey, withdrawal_credentials=deposit_data.withdrawal_credentials, @@ -175,22 +175,6 @@ def prepare_state_and_deposit(spec, state, validator_index, amount, return deposit -def build_deposit_request(spec, - index, - pubkey, - privkey, - amount, - withdrawal_credentials, - signed): - deposit_data = build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, signed=signed) - return spec.DepositRequest( - pubkey=deposit_data.pubkey, - withdrawal_credentials=deposit_data.withdrawal_credentials, - amount=deposit_data.amount, - signature=deposit_data.signature, - index=index) - - def prepare_deposit_request(spec, validator_index, amount, index=None, pubkey=None, @@ -213,69 +197,14 @@ def prepare_deposit_request(spec, validator_index, amount, if withdrawal_credentials is None: withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:] - return build_deposit_request( - spec, - index, - pubkey, - privkey, - amount, - withdrawal_credentials, - 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, - ) - - -def build_pending_deposit(spec, validator_index, amount, - index=None, - pubkey=None, - privkey=None, - withdrawal_credentials=None, - fork_version=None, - slot=None, - signed=False): - if index is None: - index = validator_index - - if pubkey is None: - pubkey = pubkeys[validator_index] - - if privkey is None: - privkey = privkeys[validator_index] - - if slot is None: - slot = spec.GENESIS_SLOT - - pending_deposit = spec.PendingDeposit( - pubkey=pubkeys[index], - withdrawal_credentials=withdrawal_credentials, - amount=amount, - slot=slot, + deposit_data = build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, signed=signed) + return spec.DepositRequest( + pubkey=deposit_data.pubkey, + withdrawal_credentials=deposit_data.withdrawal_credentials, + amount=deposit_data.amount, + signature=deposit_data.signature, + index=index ) - if signed: - deposit_data = build_deposit_data(spec, - pubkeys[index], - privkeys[index], - amount, - withdrawal_credentials, - fork_version, - signed=True) - pending_deposit.signature = deposit_data.signature - return pending_deposit def prepare_pending_deposit(spec, validator_index, amount, @@ -450,8 +379,6 @@ def run_deposit_request_processing( pre_balance = get_balance(state, validator_index) pre_effective_balance = state.validators[validator_index].effective_balance - pre_pending_deposits = len(state.pending_deposits) - yield 'pre', state yield 'deposit_request', deposit_request @@ -553,5 +480,5 @@ def run_pending_deposit_applying(spec, state, pending_deposit, validator_index, assert len(state.balances) == pre_validator_count if is_top_up: assert get_balance(state, validator_index) == pre_balance - + assert len(state.pending_deposits) == 0 diff --git a/tests/core/pyspec/eth2spec/test/helpers/state.py b/tests/core/pyspec/eth2spec/test/helpers/state.py index 07e7bfb478..72fca0b14f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/state.py +++ b/tests/core/pyspec/eth2spec/test/helpers/state.py @@ -179,3 +179,8 @@ def has_active_balance_differential(spec, state): def get_validator_index_by_pubkey(state, pubkey): index = next((i for i, validator in enumerate(state.validators) if validator.pubkey == pubkey), None) return index + + +def advance_finality_to(spec, state, epoch): + while state.finalized_checkpoint.epoch < epoch: + next_epoch_with_full_participation(spec, state) From 22bb1de62d930d98763325959b7cf321778b1dea Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 27 Aug 2024 17:32:38 +0600 Subject: [PATCH 047/103] Fix code spell --- .../electra/block_processing/test_process_deposit_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py index 220abc80fa..45fbbae2d0 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py @@ -204,7 +204,7 @@ def test_process_deposit_request_switch_to_compounding_normal(spec, state): @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 + # there is excess balance that will be enqueued to pending deposits initial_balance = initial_effective_balance = ( spec.MIN_ACTIVATION_BALANCE + spec.EFFECTIVE_BALANCE_INCREMENT // 2) initial_withdrawal_credentials = ( From 0526592fe8ed47caf7a523278452b8a9b2631a3d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 27 Aug 2024 17:46:43 +0600 Subject: [PATCH 048/103] Do state.pending_deposits update in a single line --- specs/electra/beacon-chain.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index caa1ec42ab..570dcfcc96 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -955,15 +955,13 @@ def process_pending_deposits(state: BeaconState) -> None: # Regardless of how the deposit was handled, we move on in the queue. next_deposit_index += 1 - state.pending_deposits = state.pending_deposits[next_deposit_index:] + state.pending_deposits = state.pending_deposits[next_deposit_index:] + deposits_to_postpone # Accumulate churn only if the churn limit has been hit. if is_churn_limit_reached: state.deposit_balance_to_consume = available_for_processing - processed_amount else: state.deposit_balance_to_consume = Gwei(0) - - state.pending_deposits += deposits_to_postpone ``` #### New `process_pending_consolidations` From 8c726ff723a5da6500c07d54f98ea9e8ec8fd393 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 4 Sep 2024 09:56:43 +0600 Subject: [PATCH 049/103] Fix get_max_effective_balance call --- tests/core/pyspec/eth2spec/test/helpers/deposits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index f045d1fcda..a956d29ad3 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -470,7 +470,7 @@ def run_pending_deposit_applying(spec, state, pending_deposit, validator_index, 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]) + max_effective_balace = spec.get_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 From 67cc3a5ac2cb621184b3a098c260cc95cc6abf6c Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 5 Sep 2024 14:14:23 +0600 Subject: [PATCH 050/103] Rename MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING to MAX_PENDING_DEPOSITS_PER_EPOCH --- presets/mainnet/electra.yaml | 2 +- presets/minimal/electra.yaml | 2 +- specs/electra/beacon-chain.md | 6 +++--- .../epoch_processing/test_process_pending_deposits.py | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/presets/mainnet/electra.yaml b/presets/mainnet/electra.yaml index 7b336c216c..a1a3501277 100644 --- a/presets/mainnet/electra.yaml +++ b/presets/mainnet/electra.yaml @@ -47,4 +47,4 @@ MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 8 # Pending deposits processing # --------------------------------------------------------------- # 2**4 ( = 4) pending deposits -MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: 16 +MAX_PENDING_DEPOSITS_PER_EPOCH: 16 diff --git a/presets/minimal/electra.yaml b/presets/minimal/electra.yaml index cbcb754e9c..aede73a7f9 100644 --- a/presets/minimal/electra.yaml +++ b/presets/minimal/electra.yaml @@ -47,4 +47,4 @@ MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 1 # Pending deposits processing # --------------------------------------------------------------- # 2**4 ( = 4) pending deposits -MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: 16 +MAX_PENDING_DEPOSITS_PER_EPOCH: 16 diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 89a5345e18..5d0bac5c08 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -184,7 +184,7 @@ The following values are (non-configurable) constants used throughout the specif ### Pending deposits processing | Name | Value | Description | | - | - | - | -| `MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING` | `uint64(2**4)` (= 16)| *[New in Electra:EIP6110]* Maximum number of pending deposits to process per epoch | +| `MAX_PENDING_DEPOSITS_PER_EPOCH` | `uint64(2**4)` (= 16)| *[New in Electra:EIP6110]* Maximum number of pending deposits to process per epoch | ## Configuration @@ -896,7 +896,7 @@ def apply_pending_deposit(state: BeaconState, deposit: PendingDeposit) -> None: Iterating over `pending_deposits` queue this function runs the following checks before applying pending deposit: 1. All Eth1 bridge deposits are processed before the first deposit request gets processed. 2. Deposit position in the queue is finalized. -3. Deposit does not exceed the `MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING` limit. +3. Deposit does not exceed the `MAX_PENDING_DEPOSITS_PER_EPOCH` limit. 4. Deposit does not exceed the activation churn limit. ```python @@ -924,7 +924,7 @@ def process_pending_deposits(state: BeaconState) -> None: break # Check if number of processed deposits has not reached the limit, otherwise, stop processing. - if next_deposit_index >= MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: + if next_deposit_index >= MAX_PENDING_DEPOSITS_PER_EPOCH: break # Read validator state diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py index cc78e39d50..e1f2544020 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py @@ -194,7 +194,7 @@ def test_process_pending_deposits_not_finalized(spec, state): def test_process_pending_deposits_limit_is_reached(spec, state): # set pending deposits to the maximum amount = spec.EFFECTIVE_BALANCE_INCREMENT * 1 - for i in range(spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING + 2): + for i in range(spec.MAX_PENDING_DEPOSITS_PER_EPOCH + 2): wc = state.validators[i].withdrawal_credentials pd = prepare_pending_deposit(spec, i, amount, withdrawal_credentials=wc, signed=True) state.pending_deposits.append(pd) @@ -209,10 +209,10 @@ def test_process_pending_deposits_limit_is_reached(spec, state): # deposit_balance_to_consume was reset to 0 assert state.deposit_balance_to_consume == 0 # no deposits above limit were processed - assert state.pending_deposits == new_pending_deposits[spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING:] - for i in range(spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING): + assert state.pending_deposits == new_pending_deposits[spec.MAX_PENDING_DEPOSITS_PER_EPOCH:] + for i in range(spec.MAX_PENDING_DEPOSITS_PER_EPOCH): assert state.balances[i] == pre_balances[i] + amount - for i in range(spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING, spec.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING + 2): + for i in range(spec.MAX_PENDING_DEPOSITS_PER_EPOCH, spec.MAX_PENDING_DEPOSITS_PER_EPOCH + 2): assert state.balances[i] == pre_balances[i] From 0eda70cfcbf31f5eabe95339fd43d962b00f915e Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 12 Sep 2024 20:02:39 +0600 Subject: [PATCH 051/103] Switch to compounding when consolidating with source==target --- specs/electra/beacon-chain.md | 41 +++------ .../test_process_consolidation_request.py | 84 +++++++++++++++---- .../test_process_pending_consolidations.py | 55 +----------- 3 files changed, 83 insertions(+), 97 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 3dca59e1df..d00c969f1f 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -61,7 +61,6 @@ - [Modified `get_next_sync_committee_indices`](#modified-get_next_sync_committee_indices) - [Beacon state mutators](#beacon-state-mutators) - [Modified `initiate_validator_exit`](#modified-initiate_validator_exit) - - [New `switch_to_compounding_validator`](#new-switch_to_compounding_validator) - [New `queue_excess_active_balance`](#new-queue_excess_active_balance) - [New `queue_entire_balance_and_reset_validator`](#new-queue_entire_balance_and_reset_validator) - [New `compute_exit_epoch_and_update_churn`](#new-compute_exit_epoch_and_update_churn) @@ -678,16 +677,6 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY) ``` -#### New `switch_to_compounding_validator` - -```python -def switch_to_compounding_validator(state: BeaconState, index: ValidatorIndex) -> None: - validator = state.validators[index] - if has_eth1_withdrawal_credential(validator): - validator.withdrawal_credentials = COMPOUNDING_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] - queue_excess_active_balance(state, index) -``` - #### New `queue_excess_active_balance` ```python @@ -928,8 +917,6 @@ def process_pending_consolidations(state: BeaconState) -> None: if source_validator.withdrawable_epoch > next_epoch: break - # Churn any target excess active balance of target and raise its max - switch_to_compounding_validator(state, pending_consolidation.target_index) # Move active balance to target. Excess balance is withdrawable. active_balance = get_active_balance(state, pending_consolidation.source_index) decrease_balance(state, pending_consolidation.source_index, active_balance) @@ -1225,14 +1212,6 @@ def apply_deposit(state: BeaconState, state.pending_balance_deposits.append( PendingBalanceDeposit(index=index, amount=amount) ) # [Modified in Electra:EIP7251] - # 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) - ``` ###### New `is_valid_deposit_signature` @@ -1431,10 +1410,6 @@ def process_consolidation_request( source_validator = state.validators[source_index] target_validator = state.validators[target_index] - # Verify that source != target, so a consolidation cannot be used as an exit. - if source_index == target_index: - return - # Verify source withdrawal credentials has_correct_credential = has_execution_withdrawal_credential(source_validator) is_correct_source_address = ( @@ -1459,12 +1434,22 @@ def process_consolidation_request( if target_validator.exit_epoch != FAR_FUTURE_EPOCH: return + # Churn any target excess active balance of target and raise its max + if has_eth1_withdrawal_credential(target_validator): + state.validators[target_index].withdrawal_credentials = ( + COMPOUNDING_WITHDRAWAL_PREFIX + target_validator.withdrawal_credentials[1:]) + queue_excess_active_balance(state, target_index) + + # Verify that source != target, so a consolidation cannot be used as an exit. + if source_index == target_index: + return + # Initiate source validator exit and append pending consolidation - source_validator.exit_epoch = compute_consolidation_epoch_and_update_churn( + state.validators[source_index].exit_epoch = compute_consolidation_epoch_and_update_churn( state, source_validator.effective_balance ) - source_validator.withdrawable_epoch = Epoch( - source_validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY + state.validators[source_index].withdrawable_epoch = Epoch( + state.validators[source_index].exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY ) state.pending_consolidations.append(PendingConsolidation( source_index=source_index, diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py index b05c20f565..1c6d8d22ae 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py @@ -395,7 +395,37 @@ def test_consolidation_balance_through_two_churn_epochs(spec, state): assert state.consolidation_balance_to_consume == expected_balance -# Failing tests +@with_electra_and_later +@with_presets([MINIMAL], "need sufficient consolidation churn limit") +@with_custom_state( + balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, + threshold_fn=default_activation_threshold, +) +@spec_test +@single_phase +def test_source_equals_target_switches_to_compounding(spec, state): + current_epoch = spec.get_current_epoch(state) + source_index = spec.get_active_validator_indices(state, current_epoch)[0] + + # Set source to eth1 credentials + source_address = b"\x22" * 20 + set_eth1_withdrawal_credential_with_balance( + spec, state, source_index, address=source_address + ) + # Make consolidation from source to source + consolidation = spec.ConsolidationRequest( + source_address=source_address, + source_pubkey=state.validators[source_index].pubkey, + target_pubkey=state.validators[source_index].pubkey, + ) + + # Check the the return condition + assert consolidation.source_pubkey == consolidation.target_pubkey + + yield from run_consolidation_processing( + spec, state, consolidation, success=True + ) + @with_electra_and_later @with_presets([MINIMAL], "need sufficient consolidation churn limit") @@ -405,7 +435,7 @@ def test_consolidation_balance_through_two_churn_epochs(spec, state): ) @spec_test @single_phase -def test_incorrect_source_equals_target(spec, state): +def test_source_equals_target_switches_to_compounding_with_excess(spec, state): current_epoch = spec.get_current_epoch(state) source_index = spec.get_active_validator_indices(state, current_epoch)[0] @@ -414,6 +444,8 @@ def test_incorrect_source_equals_target(spec, state): set_eth1_withdrawal_credential_with_balance( spec, state, source_index, address=source_address ) + # Add excess balance + state.balances[source_index] = state.balances[source_index] + spec.EFFECTIVE_BALANCE_INCREMENT # Make consolidation from source to source consolidation = spec.ConsolidationRequest( source_address=source_address, @@ -425,10 +457,12 @@ def test_incorrect_source_equals_target(spec, state): assert consolidation.source_pubkey == consolidation.target_pubkey yield from run_consolidation_processing( - spec, state, consolidation, success=False + spec, state, consolidation, success=True ) +# Failing tests + @with_electra_and_later @with_presets([MINIMAL], "need sufficient consolidation churn limit") @with_custom_state( @@ -815,6 +849,8 @@ def run_consolidation_processing(spec, state, consolidation, success=True): pre_exit_epoch_source = source_validator.exit_epoch pre_exit_epoch_target = target_validator.exit_epoch pre_pending_consolidations = state.pending_consolidations.copy() + pre_target_withdrawal_credentials = target_validator.withdrawal_credentials + pre_target_balance = state.balances[target_index] else: pre_state = state.copy() @@ -822,29 +858,45 @@ def run_consolidation_processing(spec, state, consolidation, success=True): yield 'consolidation_request', consolidation spec.process_consolidation_request(state, consolidation) + # print(state.validators[target_index].withdrawal_credentials) yield 'post', state if success: - # Check source and target have execution credentials + # Check source has execution credentials assert spec.has_execution_withdrawal_credential(source_validator) + # Check target has compounding credentials assert spec.has_execution_withdrawal_credential(target_validator) # Check source address in the consolidation fits the withdrawal credentials assert source_validator.withdrawal_credentials[12:] == consolidation.source_address - # Check source and target are not the same - assert source_index != target_index # Check source and target were not exiting assert pre_exit_epoch_source == spec.FAR_FUTURE_EPOCH assert pre_exit_epoch_target == spec.FAR_FUTURE_EPOCH - # Check source is now exiting - assert state.validators[source_index].exit_epoch < spec.FAR_FUTURE_EPOCH - # Check that the exit epoch matches earliest_consolidation_epoch - assert state.validators[source_index].exit_epoch == state.earliest_consolidation_epoch - # Check that the correct consolidation has been appended - expected_new_pending_consolidation = spec.PendingConsolidation( - source_index=source_index, - target_index=target_index, - ) - assert state.pending_consolidations == pre_pending_consolidations + [expected_new_pending_consolidation] + # Check excess balance is queued if the target switched to compounding + if pre_target_withdrawal_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX: + assert state.validators[target_index].withdrawal_credentials == ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + pre_target_withdrawal_credentials[1:]) + assert state.balances[target_index] == spec.MIN_ACTIVATION_BALANCE + if pre_target_balance > spec.MIN_ACTIVATION_BALANCE: + assert state.pending_balance_deposits == [spec.PendingBalanceDeposit( + index=target_index, amount=(pre_target_balance - spec.MIN_ACTIVATION_BALANCE))] + # If source and target are same, no consolidation must have been initiated + if source_index == target_index: + assert state.validators[source_index].exit_epoch == spec.FAR_FUTURE_EPOCH + assert state.pending_consolidations == [] + else: + # Check source is now exiting + assert state.validators[source_index].exit_epoch < spec.FAR_FUTURE_EPOCH + # Check that the exit epoch matches earliest_consolidation_epoch + assert state.validators[source_index].exit_epoch == state.earliest_consolidation_epoch + # Check that the withdrawable_epoch is set correctly + assert state.validators[source_index].withdrawable_epoch == ( + state.validators[source_index].exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY) + # Check that the correct consolidation has been appended + expected_new_pending_consolidation = spec.PendingConsolidation( + source_index=source_index, + target_index=target_index, + ) + assert state.pending_consolidations == pre_pending_consolidations + [expected_new_pending_consolidation] else: assert pre_state == state diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py index 14e151e2e2..61714562d9 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py @@ -36,10 +36,6 @@ def test_basic_pending_consolidation(spec, state): yield from run_epoch_processing_with(spec, state, "process_pending_consolidations") # Pending consolidation was successfully processed - assert ( - state.validators[target_index].withdrawal_credentials[:1] - == spec.COMPOUNDING_WITHDRAWAL_PREFIX - ) assert state.balances[target_index] == 2 * spec.MIN_ACTIVATION_BALANCE assert state.balances[source_index] == 0 assert state.pending_consolidations == [] @@ -65,9 +61,6 @@ def test_consolidation_not_yet_withdrawable_validator(spec, state): pre_pending_consolidations = state.pending_consolidations.copy() pre_balances = state.balances.copy() - pre_target_withdrawal_credential = state.validators[ - target_index - ].withdrawal_credentials[:1] yield from run_epoch_processing_with(spec, state, "process_pending_consolidations") @@ -75,11 +68,6 @@ def test_consolidation_not_yet_withdrawable_validator(spec, state): # Balances are unchanged assert state.balances[source_index] == pre_balances[0] assert state.balances[target_index] == pre_balances[1] - # Target withdrawal credential is unchanged - assert ( - state.validators[target_index].withdrawal_credentials[:1] - == pre_target_withdrawal_credential - ) # Pending consolidation is still in the queue assert state.pending_consolidations == pre_pending_consolidations @@ -121,17 +109,9 @@ def test_skip_consolidation_when_source_slashed(spec, state): # first pending consolidation should not be processed assert state.balances[target0_index] == spec.MIN_ACTIVATION_BALANCE assert state.balances[source0_index] == spec.MIN_ACTIVATION_BALANCE - assert ( - state.validators[target0_index].withdrawal_credentials[:1] - == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX - ) # second pending consolidation should be processed: first one is skipped and doesn't block the queue assert state.balances[target1_index] == 2 * spec.MIN_ACTIVATION_BALANCE assert state.balances[source1_index] == 0 - assert ( - state.validators[target1_index].withdrawal_credentials[:1] - == spec.COMPOUNDING_WITHDRAWAL_PREFIX - ) @with_electra_and_later @@ -167,26 +147,14 @@ def test_all_consolidation_cases_together(spec, state): spec.initiate_validator_exit(state, 2) pre_balances = state.balances.copy() - pre_target_withdrawal_prefixes = [ - state.validators[target_index[i]].withdrawal_credentials[:1] - for i in [0, 1, 2, 3] - ] pre_pending_consolidations = state.pending_consolidations.copy() yield from run_epoch_processing_with(spec, state, "process_pending_consolidations") # First consolidation is successfully processed - assert ( - state.validators[target_index[0]].withdrawal_credentials[:1] - == spec.COMPOUNDING_WITHDRAWAL_PREFIX - ) assert state.balances[target_index[0]] == 2 * spec.MIN_ACTIVATION_BALANCE assert state.balances[source_index[0]] == 0 # All other consolidations are not processed for i in [1, 2, 3]: - assert ( - state.validators[target_index[i]].withdrawal_credentials[:1] - == pre_target_withdrawal_prefixes[i] - ) assert state.balances[source_index[i]] == pre_balances[source_index[i]] assert state.balances[target_index[i]] == pre_balances[target_index[i]] # First consolidation is processed, second is skipped, last two are left in the queue @@ -226,22 +194,11 @@ def test_pending_consolidation_future_epoch(spec, state): # Pending consolidation was successfully processed expected_source_balance = state_before_consolidation.balances[source_index] - spec.MIN_ACTIVATION_BALANCE - assert ( - state.validators[target_index].withdrawal_credentials[:1] - == spec.COMPOUNDING_WITHDRAWAL_PREFIX - ) - assert state.balances[target_index] == 2 * spec.MIN_ACTIVATION_BALANCE + expected_target_balance = state_before_consolidation.balances[target_index] + spec.MIN_ACTIVATION_BALANCE assert state.balances[source_index] == expected_source_balance + assert state.balances[target_index] == expected_target_balance assert state.pending_consolidations == [] - # Pending balance deposit to the target is created as part of `switch_to_compounding_validator`. - # The excess balance to queue are the rewards accumulated over the previous epoch transitions. - expected_pending_balance = state_before_consolidation.balances[target_index] - spec.MIN_ACTIVATION_BALANCE - assert len(state.pending_balance_deposits) > 0 - pending_balance_deposit = state.pending_balance_deposits[len(state.pending_balance_deposits) - 1] - assert pending_balance_deposit.index == target_index - assert pending_balance_deposit.amount == expected_pending_balance - @with_electra_and_later @spec_state_test @@ -280,10 +237,6 @@ def test_pending_consolidation_compounding_creds(spec, state): expected_target_balance = ( state_before_consolidation.balances[source_index] + state_before_consolidation.balances[target_index] ) - assert ( - state.validators[target_index].withdrawal_credentials[:1] - == spec.COMPOUNDING_WITHDRAWAL_PREFIX - ) assert state.balances[target_index] == expected_target_balance # All source balance is active and moved to the target, # because the source validator has compounding credentials @@ -336,10 +289,6 @@ def test_pending_consolidation_with_pending_deposit(spec, state): expected_target_balance = ( state_before_consolidation.balances[source_index] + state_before_consolidation.balances[target_index] ) - assert ( - state.validators[target_index].withdrawal_credentials[:1] - == spec.COMPOUNDING_WITHDRAWAL_PREFIX - ) assert state.balances[target_index] == expected_target_balance assert state.balances[source_index] == 0 assert state.pending_consolidations == [] From 747a5a7891a35b6dccbd8b6379ecb9c24936f4ee Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 16 Sep 2024 11:01:06 +0400 Subject: [PATCH 052/103] Apply suggestions from @dapplion Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com> --- specs/electra/beacon-chain.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index d00c969f1f..9037dd74df 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -1437,7 +1437,8 @@ def process_consolidation_request( # Churn any target excess active balance of target and raise its max if has_eth1_withdrawal_credential(target_validator): state.validators[target_index].withdrawal_credentials = ( - COMPOUNDING_WITHDRAWAL_PREFIX + target_validator.withdrawal_credentials[1:]) + COMPOUNDING_WITHDRAWAL_PREFIX + target_validator.withdrawal_credentials[1:] + ) queue_excess_active_balance(state, target_index) # Verify that source != target, so a consolidation cannot be used as an exit. From 1bf8ca5777cab1327036cea52d559ceee76e47f5 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 18 Sep 2024 09:20:42 +0400 Subject: [PATCH 053/103] Apply suggestions by @ralexstokes Co-authored-by: Alex Stokes --- .../block_processing/test_process_consolidation_request.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py index 1c6d8d22ae..dd4f0b944b 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py @@ -858,7 +858,6 @@ def run_consolidation_processing(spec, state, consolidation, success=True): yield 'consolidation_request', consolidation spec.process_consolidation_request(state, consolidation) - # print(state.validators[target_index].withdrawal_credentials) yield 'post', state From b29a1d36d1a119db2bebd8e10d3244551be3c3f0 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 18 Sep 2024 13:27:03 +0400 Subject: [PATCH 054/103] Isolate switch to compounding flow --- specs/electra/beacon-chain.md | 81 +++- .../test_process_consolidation_request.py | 396 ++++++++++++++++-- .../eth2spec/test/helpers/withdrawals.py | 5 +- 3 files changed, 425 insertions(+), 57 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 9037dd74df..10c0eebf3a 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -61,6 +61,7 @@ - [Modified `get_next_sync_committee_indices`](#modified-get_next_sync_committee_indices) - [Beacon state mutators](#beacon-state-mutators) - [Modified `initiate_validator_exit`](#modified-initiate_validator_exit) + - [New `switch_to_compounding_validator`](#new-switch_to_compounding_validator) - [New `queue_excess_active_balance`](#new-queue_excess_active_balance) - [New `queue_entire_balance_and_reset_validator`](#new-queue_entire_balance_and_reset_validator) - [New `compute_exit_epoch_and_update_churn`](#new-compute_exit_epoch_and_update_churn) @@ -96,6 +97,7 @@ - [Deposit requests](#deposit-requests) - [New `process_deposit_request`](#new-process_deposit_request) - [Execution layer consolidation requests](#execution-layer-consolidation-requests) + - [New `is_valid_switch_to_compounding_request`](#new-is_valid_switch_to_compounding_request) - [New `process_consolidation_request`](#new-process_consolidation_request) - [Testing](#testing) @@ -677,6 +679,15 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY) ``` +#### New `switch_to_compounding_validator` + +```python +def switch_to_compounding_validator(state: BeaconState, index: ValidatorIndex) -> None: + validator = state.validators[index] + validator.withdrawal_credentials = COMPOUNDING_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] + queue_excess_active_balance(state, index) +``` + #### New `queue_excess_active_balance` ```python @@ -1383,6 +1394,45 @@ def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) ##### Execution layer consolidation requests +###### New `is_valid_switch_to_compounding_request` + +```python +def is_valid_switch_to_compounding_request( + state: BeaconState, + consolidation_request: ConsolidationRequest +) -> bool: + # Switch to compounding requires source and target be equal + if consolidation_request.source_pubkey != consolidation_request.target_pubkey: + return False + + # Verify pubkey exists + source_pubkey = consolidation_request.source_pubkey + validator_pubkeys = [v.pubkey for v in state.validators] + if source_pubkey not in validator_pubkeys: + return False + + source_validator = state.validators[ValidatorIndex(validator_pubkeys.index(source_pubkey))] + + # Verify request has been authorized + if source_validator.withdrawal_credentials[12:] != consolidation_request.source_address: + return False + + # Verify source withdrawal credentials + if not has_eth1_withdrawal_credential(source_validator): + return False + + # Verify the source is active + current_epoch = get_current_epoch(state) + if not is_active_validator(source_validator, current_epoch): + return False + + # Verify exit for source have not been initiated + if source_validator.exit_epoch != FAR_FUTURE_EPOCH: + return False + + return True +``` + ###### New `process_consolidation_request` ```python @@ -1390,6 +1440,16 @@ def process_consolidation_request( state: BeaconState, consolidation_request: ConsolidationRequest ) -> None: + if is_valid_switch_to_compounding_request(state, consolidation_request): + validator_pubkeys = [v.pubkey for v in state.validators] + request_source_pubkey = consolidation_request.source_pubkey + source_index = ValidatorIndex(validator_pubkeys.index(request_source_pubkey)) + switch_to_compounding_validator(state, source_index) + return + + # Verify that source != target, so a consolidation cannot be used as an exit. + if consolidation_request.source_pubkey == consolidation_request.target_pubkey: + return # If the pending consolidations queue is full, consolidation requests are ignored if len(state.pending_consolidations) == PENDING_CONSOLIDATIONS_LIMIT: return @@ -1434,28 +1494,21 @@ def process_consolidation_request( if target_validator.exit_epoch != FAR_FUTURE_EPOCH: return - # Churn any target excess active balance of target and raise its max - if has_eth1_withdrawal_credential(target_validator): - state.validators[target_index].withdrawal_credentials = ( - COMPOUNDING_WITHDRAWAL_PREFIX + target_validator.withdrawal_credentials[1:] - ) - queue_excess_active_balance(state, target_index) - - # Verify that source != target, so a consolidation cannot be used as an exit. - if source_index == target_index: - return - # Initiate source validator exit and append pending consolidation - state.validators[source_index].exit_epoch = compute_consolidation_epoch_and_update_churn( + source_validator.exit_epoch = compute_consolidation_epoch_and_update_churn( state, source_validator.effective_balance ) - state.validators[source_index].withdrawable_epoch = Epoch( - state.validators[source_index].exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY + source_validator.withdrawable_epoch = Epoch( + source_validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY ) state.pending_consolidations.append(PendingConsolidation( source_index=source_index, target_index=target_index )) + + # Churn any target excess active balance of target and raise its max + if has_eth1_withdrawal_credential(target_validator): + switch_to_compounding_validator(state, target_index) ``` ## Testing diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py index dd4f0b944b..4bc4617c82 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py @@ -66,6 +66,106 @@ def test_basic_consolidation_in_current_consolidation_epoch(spec, state): assert state.validators[source_index].exit_epoch == expected_exit_epoch +@with_electra_and_later +@with_presets([MINIMAL], "need sufficient consolidation churn limit") +@with_custom_state( + balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, + threshold_fn=default_activation_threshold, +) +@spec_test +@single_phase +def test_basic_consolidation_with_excess_target_balance(spec, state): + # This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn + current_epoch = spec.get_current_epoch(state) + source_index = spec.get_active_validator_indices(state, current_epoch)[0] + target_index = spec.get_active_validator_indices(state, current_epoch)[1] + + # Set source to eth1 credentials + source_address = b"\x22" * 20 + set_eth1_withdrawal_credential_with_balance( + spec, state, source_index, address=source_address + ) + # Make consolidation with source address + consolidation = spec.ConsolidationRequest( + source_address=source_address, + source_pubkey=state.validators[source_index].pubkey, + target_pubkey=state.validators[target_index].pubkey, + ) + + # Set target to eth1 credentials + set_eth1_withdrawal_credential_with_balance(spec, state, target_index) + + # Set earliest consolidation epoch to the expected exit epoch + expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + state.earliest_consolidation_epoch = expected_exit_epoch + consolidation_churn_limit = spec.get_consolidation_churn_limit(state) + # Set the consolidation balance to consume equal to churn limit + state.consolidation_balance_to_consume = consolidation_churn_limit + + # Add excess balance + state.balances[target_index] = state.balances[target_index] + spec.EFFECTIVE_BALANCE_INCREMENT + + yield from run_consolidation_processing(spec, state, consolidation) + + # Check consolidation churn is decremented correctly + assert ( + state.consolidation_balance_to_consume + == consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE + ) + # Check exit epoch + assert state.validators[source_index].exit_epoch == expected_exit_epoch + + +@with_electra_and_later +@with_presets([MINIMAL], "need sufficient consolidation churn limit") +@with_custom_state( + balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, + threshold_fn=default_activation_threshold, +) +@spec_test +@single_phase +def test_basic_consolidation_with_excess_target_balance_and_compounding_credentials(spec, state): + # This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn + current_epoch = spec.get_current_epoch(state) + source_index = spec.get_active_validator_indices(state, current_epoch)[0] + target_index = spec.get_active_validator_indices(state, current_epoch)[1] + + # Set source to eth1 credentials + source_address = b"\x22" * 20 + set_eth1_withdrawal_credential_with_balance( + spec, state, source_index, address=source_address + ) + # Make consolidation with source address + consolidation = spec.ConsolidationRequest( + source_address=source_address, + source_pubkey=state.validators[source_index].pubkey, + target_pubkey=state.validators[target_index].pubkey, + ) + + # Set target to eth1 credentials + set_compounding_withdrawal_credential(spec, state, target_index) + + # Set earliest consolidation epoch to the expected exit epoch + expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + state.earliest_consolidation_epoch = expected_exit_epoch + consolidation_churn_limit = spec.get_consolidation_churn_limit(state) + # Set the consolidation balance to consume equal to churn limit + state.consolidation_balance_to_consume = consolidation_churn_limit + + # Add excess balance + state.balances[target_index] = state.balances[target_index] + spec.EFFECTIVE_BALANCE_INCREMENT + + yield from run_consolidation_processing(spec, state, consolidation) + + # Check consolidation churn is decremented correctly + assert ( + state.consolidation_balance_to_consume + == consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE + ) + # Check exit epoch + assert state.validators[source_index].exit_epoch == expected_exit_epoch + + @with_electra_and_later @with_presets([MINIMAL], "need sufficient consolidation churn limit") @with_custom_state( @@ -235,7 +335,7 @@ def test_basic_consolidation_with_compounding_credentials(spec, state): target_pubkey=state.validators[target_index].pubkey, ) - # Set target to eth1 credentials + # Set target to compounding credentials set_compounding_withdrawal_credential(spec, state, target_index) # Set the consolidation balance to consume equal to churn limit @@ -396,14 +496,8 @@ def test_consolidation_balance_through_two_churn_epochs(spec, state): @with_electra_and_later -@with_presets([MINIMAL], "need sufficient consolidation churn limit") -@with_custom_state( - balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, - threshold_fn=default_activation_threshold, -) -@spec_test -@single_phase -def test_source_equals_target_switches_to_compounding(spec, state): +@spec_state_test +def test_basic_switch_to_compounding(spec, state): current_epoch = spec.get_current_epoch(state) source_index = spec.get_active_validator_indices(state, current_epoch)[0] @@ -419,23 +513,14 @@ def test_source_equals_target_switches_to_compounding(spec, state): target_pubkey=state.validators[source_index].pubkey, ) - # Check the the return condition - assert consolidation.source_pubkey == consolidation.target_pubkey - - yield from run_consolidation_processing( + yield from run_switch_to_compounding_processing( spec, state, consolidation, success=True ) @with_electra_and_later -@with_presets([MINIMAL], "need sufficient consolidation churn limit") -@with_custom_state( - balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, - threshold_fn=default_activation_threshold, -) -@spec_test -@single_phase -def test_source_equals_target_switches_to_compounding_with_excess(spec, state): +@spec_state_test +def test_switch_to_compounding_with_excess(spec, state): current_epoch = spec.get_current_epoch(state) source_index = spec.get_active_validator_indices(state, current_epoch)[0] @@ -453,10 +538,36 @@ def test_source_equals_target_switches_to_compounding_with_excess(spec, state): target_pubkey=state.validators[source_index].pubkey, ) - # Check the the return condition - assert consolidation.source_pubkey == consolidation.target_pubkey + yield from run_switch_to_compounding_processing( + spec, state, consolidation, success=True + ) - yield from run_consolidation_processing( + +@with_electra_and_later +@spec_state_test +def test_switch_to_compounding_with_pending_consolidations_at_limit(spec, state): + state.pending_consolidations = [ + spec.PendingConsolidation(source_index=0, target_index=1) + ] * spec.PENDING_CONSOLIDATIONS_LIMIT + + current_epoch = spec.get_current_epoch(state) + source_index = spec.get_active_validator_indices(state, current_epoch)[0] + + # Set source to eth1 credentials + source_address = b"\x22" * 20 + set_eth1_withdrawal_credential_with_balance( + spec, state, source_index, address=source_address + ) + # Add excess balance + state.balances[source_index] = state.balances[source_index] + spec.EFFECTIVE_BALANCE_INCREMENT + # Make consolidation from source to source + consolidation = spec.ConsolidationRequest( + source_address=source_address, + source_pubkey=state.validators[source_index].pubkey, + target_pubkey=state.validators[source_index].pubkey, + ) + + yield from run_switch_to_compounding_processing( spec, state, consolidation, success=True ) @@ -831,6 +942,156 @@ def test_incorrect_unknown_target_pubkey(spec, state): ) +@with_electra_and_later +@spec_state_test +def test_switch_to_compounding_exited_source(spec, state): + # Set up an otherwise correct request + current_epoch = spec.get_current_epoch(state) + source_index = spec.get_active_validator_indices(state, current_epoch)[0] + source_address = b"\x22" * 20 + set_eth1_withdrawal_credential_with_balance( + spec, state, source_index, address=source_address + ) + consolidation = spec.ConsolidationRequest( + source_address=source_address, + source_pubkey=state.validators[source_index].pubkey, + target_pubkey=state.validators[source_index].pubkey, + ) + + # exit source + spec.initiate_validator_exit(state, source_index) + + # Check the the return condition + assert state.validators[source_index].exit_epoch != spec.FAR_FUTURE_EPOCH + + yield from run_switch_to_compounding_processing( + spec, state, consolidation, success=False + ) + + +@with_electra_and_later +@spec_state_test +def test_switch_to_compounding_inactive_source(spec, state): + # Set up an otherwise correct request + current_epoch = spec.get_current_epoch(state) + source_index = spec.get_active_validator_indices(state, current_epoch)[0] + source_address = b"\x22" * 20 + set_eth1_withdrawal_credential_with_balance( + spec, state, source_index, address=source_address + ) + consolidation = spec.ConsolidationRequest( + source_address=source_address, + source_pubkey=state.validators[source_index].pubkey, + target_pubkey=state.validators[source_index].pubkey, + ) + + # set source validator as not yet activated + state.validators[source_index].activation_epoch = spec.FAR_FUTURE_EPOCH + + # Check the the return condition + assert not spec.is_active_validator(state.validators[source_index], current_epoch) + + yield from run_switch_to_compounding_processing( + spec, state, consolidation, success=False + ) + + +@with_electra_and_later +@spec_state_test +def test_switch_to_compounding_source_bls_withdrawal_credential(spec, state): + # Set up a correct request, but source does have + # a bls withdrawal credential + current_epoch = spec.get_current_epoch(state) + source_index = spec.get_active_validator_indices(state, current_epoch)[0] + source_address = b"\x22" * 20 + consolidation = spec.ConsolidationRequest( + source_address=source_address, + source_pubkey=state.validators[source_index].pubkey, + target_pubkey=state.validators[source_index].pubkey, + ) + + # Check the the return condition + assert not spec.has_eth1_withdrawal_credential(state.validators[source_index]) + + yield from run_switch_to_compounding_processing( + spec, state, consolidation, success=False + ) + + +@with_electra_and_later +@spec_state_test +def test_switch_to_compounding_source_coumpounding_withdrawal_credential(spec, state): + # Set up a correct request, but source does have + # a compounding withdrawal credential and excess balance + current_epoch = spec.get_current_epoch(state) + source_index = spec.get_active_validator_indices(state, current_epoch)[0] + source_address = b"\x22" * 20 + consolidation = spec.ConsolidationRequest( + source_address=source_address, + source_pubkey=state.validators[source_index].pubkey, + target_pubkey=state.validators[source_index].pubkey, + ) + set_compounding_withdrawal_credential(spec, state, source_index) + state.balances[source_index] = spec.MIN_ACTIVATION_BALANCE + spec.EFFECTIVE_BALANCE_INCREMENT + + # Check the the return condition + assert not spec.has_eth1_withdrawal_credential(state.validators[source_index]) + + yield from run_switch_to_compounding_processing( + spec, state, consolidation, success=False + ) + + +@with_electra_and_later +@spec_state_test +def test_switch_to_compounding_not_authorized(spec, state): + # Set up an otherwise correct request + current_epoch = spec.get_current_epoch(state) + source_index = spec.get_active_validator_indices(state, current_epoch)[0] + source_address = b"\x22" * 20 + set_eth1_withdrawal_credential_with_balance( + spec, state, source_index, address=source_address + ) + # Make request with different source address + consolidation = spec.ConsolidationRequest( + source_address=b"\x33" * 20, + source_pubkey=state.validators[source_index].pubkey, + target_pubkey=state.validators[source_index].pubkey, + ) + + # Check the the return condition + assert not state.validators[source_index].withdrawal_credentials[12:] == consolidation.source_address + + yield from run_switch_to_compounding_processing( + spec, state, consolidation, success=False + ) + + +@with_electra_and_later +@spec_state_test +def test_switch_to_compounding_unknown_source_pubkey(spec, state): + # Set up an otherwise correct request + current_epoch = spec.get_current_epoch(state) + source_index = spec.get_active_validator_indices(state, current_epoch)[0] + source_address = b"\x22" * 20 + set_eth1_withdrawal_credential_with_balance( + spec, state, source_index, address=source_address + ) + # Make consolidation with different source pubkey + consolidation = spec.ConsolidationRequest( + source_address=source_address, + source_pubkey=b"\x00" * 48, + target_pubkey=b"\x00" * 48, + ) + + # Check the the return condition + assert not state.validators[source_index].pubkey == consolidation.source_pubkey + + yield from run_switch_to_compounding_processing( + spec, state, consolidation, success=False + ) + + def run_consolidation_processing(spec, state, consolidation, success=True): """ Run ``process_consolidation``, yielding: @@ -865,12 +1126,28 @@ def run_consolidation_processing(spec, state, consolidation, success=True): # Check source has execution credentials assert spec.has_execution_withdrawal_credential(source_validator) # Check target has compounding credentials - assert spec.has_execution_withdrawal_credential(target_validator) + assert spec.has_compounding_withdrawal_credential(state.validators[target_index]) # Check source address in the consolidation fits the withdrawal credentials assert source_validator.withdrawal_credentials[12:] == consolidation.source_address + # Check source and target are not the same + assert source_index != target_index # Check source and target were not exiting assert pre_exit_epoch_source == spec.FAR_FUTURE_EPOCH assert pre_exit_epoch_target == spec.FAR_FUTURE_EPOCH + # Check source is now exiting + assert state.validators[source_index].exit_epoch < spec.FAR_FUTURE_EPOCH + # Check that the exit epoch matches earliest_consolidation_epoch + assert state.validators[source_index].exit_epoch == state.earliest_consolidation_epoch + # Check that the withdrawable_epoch is set correctly + assert state.validators[source_index].withdrawable_epoch == ( + state.validators[source_index].exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY + ) + # Check that the correct consolidation has been appended + expected_new_pending_consolidation = spec.PendingConsolidation( + source_index=source_index, + target_index=target_index, + ) + assert state.pending_consolidations == pre_pending_consolidations + [expected_new_pending_consolidation] # Check excess balance is queued if the target switched to compounding if pre_target_withdrawal_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX: assert state.validators[target_index].withdrawal_credentials == ( @@ -879,23 +1156,58 @@ def run_consolidation_processing(spec, state, consolidation, success=True): if pre_target_balance > spec.MIN_ACTIVATION_BALANCE: assert state.pending_balance_deposits == [spec.PendingBalanceDeposit( index=target_index, amount=(pre_target_balance - spec.MIN_ACTIVATION_BALANCE))] - # If source and target are same, no consolidation must have been initiated - if source_index == target_index: - assert state.validators[source_index].exit_epoch == spec.FAR_FUTURE_EPOCH - assert state.pending_consolidations == [] else: - # Check source is now exiting - assert state.validators[source_index].exit_epoch < spec.FAR_FUTURE_EPOCH - # Check that the exit epoch matches earliest_consolidation_epoch - assert state.validators[source_index].exit_epoch == state.earliest_consolidation_epoch - # Check that the withdrawable_epoch is set correctly - assert state.validators[source_index].withdrawable_epoch == ( - state.validators[source_index].exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY) - # Check that the correct consolidation has been appended - expected_new_pending_consolidation = spec.PendingConsolidation( - source_index=source_index, - target_index=target_index, - ) - assert state.pending_consolidations == pre_pending_consolidations + [expected_new_pending_consolidation] + assert state.balances[target_index] == pre_target_balance + else: + assert pre_state == state + + +def run_switch_to_compounding_processing(spec, state, consolidation, success=True): + """ + Run ``process_consolidation``, yielding: + - pre-state ('pre') + - consolidation_request ('consolidation_request') + - post-state ('post'). + If ``success == False``, ``process_consolidation_request`` would return without any state change. + """ + + if success: + validator_pubkeys = [v.pubkey for v in state.validators] + source_index = spec.ValidatorIndex(validator_pubkeys.index(consolidation.source_pubkey)) + target_index = spec.ValidatorIndex(validator_pubkeys.index(consolidation.target_pubkey)) + source_validator = state.validators[source_index] + pre_exit_epoch = source_validator.exit_epoch + pre_pending_consolidations = state.pending_consolidations.copy() + pre_withdrawal_credentials = source_validator.withdrawal_credentials + pre_balance = state.balances[source_index] + else: + pre_state = state.copy() + + yield 'pre', state + yield 'consolidation_request', consolidation + + spec.process_consolidation_request(state, consolidation) + + yield 'post', state + + if success: + # Check that source and target are same + assert source_index == target_index + # Check that the credentials before the switch are of ETH1 type + assert pre_withdrawal_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + # Check source address in the consolidation fits the withdrawal credentials + assert state.validators[source_index].withdrawal_credentials[12:] == consolidation.source_address + # Check that the source has switched to compounding + assert state.validators[source_index].withdrawal_credentials == ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + pre_withdrawal_credentials[1:] + ) + # Check excess balance is queued + assert state.balances[source_index] == spec.MIN_ACTIVATION_BALANCE + if pre_balance > spec.MIN_ACTIVATION_BALANCE: + assert state.pending_balance_deposits == [spec.PendingBalanceDeposit( + index=source_index, amount=(pre_balance - spec.MIN_ACTIVATION_BALANCE))] + # Check no consolidation has been initiated + assert state.validators[source_index].exit_epoch == spec.FAR_FUTURE_EPOCH + assert state.pending_consolidations == pre_pending_consolidations else: assert pre_state == state diff --git a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py index 0ce476c86f..6247c54761 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py @@ -30,7 +30,10 @@ def set_validator_fully_withdrawable(spec, state, index, withdrawable_epoch=None def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance=None, address=None): if balance is None: - balance = spec.MAX_EFFECTIVE_BALANCE + if is_post_electra(spec): + balance = spec.MIN_ACTIVATION_BALANCE + else: + balance = spec.MAX_EFFECTIVE_BALANCE if address is None: address = b'\x11' * 20 From d71d9dda9e093b1421f2077be1ba4c316852811a Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 18 Sep 2024 13:33:18 +0400 Subject: [PATCH 055/103] Fix lint --- .../block_processing/test_process_consolidation_request.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py index 4bc4617c82..3259bc2d11 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py @@ -1176,7 +1176,6 @@ def run_switch_to_compounding_processing(spec, state, consolidation, success=Tru source_index = spec.ValidatorIndex(validator_pubkeys.index(consolidation.source_pubkey)) target_index = spec.ValidatorIndex(validator_pubkeys.index(consolidation.target_pubkey)) source_validator = state.validators[source_index] - pre_exit_epoch = source_validator.exit_epoch pre_pending_consolidations = state.pending_consolidations.copy() pre_withdrawal_credentials = source_validator.withdrawal_credentials pre_balance = state.balances[source_index] From 083402fe7db8052daf38dc0516af79ddb910d57e Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 18 Sep 2024 16:23:27 +0400 Subject: [PATCH 056/103] Remove switch_to_compounding from deposit flow --- specs/electra/beacon-chain.md | 18 -- .../test_process_deposit_request.py | 244 ------------------ .../pyspec/eth2spec/test/helpers/deposits.py | 24 +- 3 files changed, 3 insertions(+), 283 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 1c22bab845..018d415d4b 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -1481,24 +1481,6 @@ def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) signature=deposit_request.signature, slot=state.slot, )) - - # If validator is active, check switch to compounding - validator_pubkeys = [v.pubkey for v in state.validators] - if deposit_request.pubkey in validator_pubkeys: - validator_index = ValidatorIndex(validator_pubkeys.index(deposit_request.pubkey)) - validator = state.validators[validator_index] - if ( - validator.exit_epoch > get_current_epoch(state) - and is_compounding_withdrawal_credential(deposit_request.withdrawal_credentials) - and has_eth1_withdrawal_credential(validator) - and is_valid_deposit_signature( - deposit_request.pubkey, - deposit_request.withdrawal_credentials, - deposit_request.amount, - deposit_request.signature - ) - ): - switch_to_compounding_validator(state, validator_index) ``` ##### Execution layer consolidation requests diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py index 45fbbae2d0..930bcd5770 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py @@ -5,41 +5,6 @@ ) -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_process_deposit_request_min_activation(spec, state): @@ -173,212 +138,3 @@ def test_process_deposit_request_set_start_index_only_once(spec, state): 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 - ) - 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=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 balance 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_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_switching_to_compounding( - spec, - state, - validator_index, - initial_withdrawal_credentials, - compounding_credentials, - signed=False, - effective=False - ) - - -@with_electra_and_later -@spec_state_test -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 - ) - - # 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) - - 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 -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 - ) - - # Initiate exit - spec.initiate_validator_exit(state, validator_index) - - yield from _run_deposit_request_switching_to_compounding( - spec, - state, - validator_index, - initial_withdrawal_credentials, - compounding_credentials, - effective=True - ) diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index a956d29ad3..bd0d670491 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -357,8 +357,7 @@ def run_deposit_request_processing( state, deposit_request, validator_index, - effective=True, - switches_to_compounding=False): + effective=True): """ Run ``process_deposit_request``, yielding: @@ -367,8 +366,6 @@ def run_deposit_request_processing( - post-state ('post'). """ assert is_post_electra(spec) - if switches_to_compounding: - assert effective pre_validator_count = len(state.validators) pre_balance = 0 @@ -392,10 +389,7 @@ def run_deposit_request_processing( 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 + assert state.balances[validator_index] == pre_balance pending_deposit = spec.PendingDeposit( pubkey=deposit_request.pubkey, @@ -405,19 +399,7 @@ def run_deposit_request_processing( 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] + assert state.pending_deposits == [pending_deposit] def run_pending_deposit_applying(spec, state, pending_deposit, validator_index, effective=True): From 23699f596da62980d650c63f9a47feec247c8f56 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 19 Sep 2024 10:44:38 +0400 Subject: [PATCH 057/103] Undo MAX_EB to MIN_AB switch in withdrawal helper --- tests/core/pyspec/eth2spec/test/helpers/withdrawals.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py index 6247c54761..0ce476c86f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py @@ -30,10 +30,7 @@ def set_validator_fully_withdrawable(spec, state, index, withdrawable_epoch=None def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance=None, address=None): if balance is None: - if is_post_electra(spec): - balance = spec.MIN_ACTIVATION_BALANCE - else: - balance = spec.MAX_EFFECTIVE_BALANCE + balance = spec.MAX_EFFECTIVE_BALANCE if address is None: address = b'\x11' * 20 From 0c5ad81145fea58504b6db9e8c73214a9fcb331a Mon Sep 17 00:00:00 2001 From: Echo <78665754+EchoAlice@users.noreply.github.com> Date: Thu, 19 Sep 2024 19:34:14 -0600 Subject: [PATCH 058/103] Fix merkle multiproof documentation (#3928) --- ssz/merkle-proofs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssz/merkle-proofs.md b/ssz/merkle-proofs.md index 9b9127d63a..ffc543f95e 100644 --- a/ssz/merkle-proofs.md +++ b/ssz/merkle-proofs.md @@ -257,7 +257,7 @@ We define a Merkle multiproof as a minimal subset of nodes in a Merkle tree need x x . . . . x * ``` -. are unused nodes, * are used nodes, x are the values we are trying to prove. Notice how despite being a multiproof for 3 values, it requires only 3 auxiliary nodes, only one node more than would be required to prove a single value. Normally the efficiency gains are not quite that extreme, but the savings relative to individual Merkle proofs are still significant. As a rule of thumb, a multiproof for k nodes at the same level of an n-node tree has size `k * (n/k + log(n/k))`. +. are unused nodes, * are used nodes, x are the values we are trying to prove. Notice how despite being a multiproof for 3 values, it requires only 3 auxiliary nodes, the same amount required to prove a single value. Normally the efficiency gains are not quite that extreme, but the savings relative to individual Merkle proofs are still significant. As a rule of thumb, a multiproof for k nodes at the same level of an n-node tree has size `k * (n/k + log(n/k))`. First, we provide a method for computing the generalized indices of the auxiliary tree nodes that a proof of a given set of generalized indices will require: From 0c9762f8375259465d52a107f06c215ece8aeb46 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Fri, 20 Sep 2024 09:23:01 -0500 Subject: [PATCH 059/103] Improve check_toc rule in Makefile --- Makefile | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index c805b456b0..f424832ca1 100644 --- a/Makefile +++ b/Makefile @@ -28,12 +28,6 @@ FORK_CHOICE_DIR = ./fork_choice # To check generator matching: #$(info $$GENERATOR_TARGETS is [${GENERATOR_TARGETS}]) -MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \ - $(wildcard $(SPEC_DIR)/*/*/*.md) \ - $(wildcard $(SPEC_DIR)/_features/*/*.md) \ - $(wildcard $(SPEC_DIR)/_features/*/*/*.md) \ - $(wildcard $(SSZ_DIR)/*.md) - ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra whisk eip6800 eip7732 # The parameters for commands. Use `foreach` to avoid listing specs again. COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), --cov=eth2spec.$S.$(TEST_PRESET_TYPE)) @@ -141,13 +135,33 @@ endif open_cov: ((open "$(COV_INDEX_FILE)" || xdg-open "$(COV_INDEX_FILE)") &> /dev/null) & +############################################################################### +# Table of Contents +############################################################################### + +# When using .ONESHELL, bail on errors. +.SHELLFLAGS = -e + +# This contains all markdown files with specifications. +MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/**/*.md) \ + $(wildcard $(SSZ_DIR)/*.md) + +# Check all files and error if any ToC were modified. check_toc: $(MARKDOWN_FILES:=.toc) + @[ "$$(find . -name '*.md.tmp' -print -quit)" ] && exit 1 || exit 0 +# Generate ToC sections & save copy of original if modified. +.ONESHELL: %.toc: - cp $* $*.tmp && \ - doctoc $* && \ - diff -q $* $*.tmp && \ - rm $*.tmp + @cp $* $*.tmp + @doctoc $* > /dev/null + @if diff -q $* $*.tmp > /dev/null; then \ + echo "Good $*"; \ + rm $*.tmp; \ + else \ + echo "\033[1;33m Bad $*\033[0m"; \ + echo "\033[1;34m See $*.tmp\033[0m"; \ + fi codespell: codespell . --skip "./.git,./venv,$(PY_SPEC_DIR)/.mypy_cache" -I .codespell-whitelist From e0651d34b0c43a2e34bb3092c04f80a18e63cc03 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Fri, 20 Sep 2024 09:35:44 -0500 Subject: [PATCH 060/103] Revert some changes --- Makefile | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index f424832ca1..dbda9104a2 100644 --- a/Makefile +++ b/Makefile @@ -135,15 +135,14 @@ endif open_cov: ((open "$(COV_INDEX_FILE)" || xdg-open "$(COV_INDEX_FILE)") &> /dev/null) & -############################################################################### -# Table of Contents -############################################################################### - # When using .ONESHELL, bail on errors. .SHELLFLAGS = -e -# This contains all markdown files with specifications. -MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/**/*.md) \ +# This contains all markdown files that should have table of contents. +MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \ + $(wildcard $(SPEC_DIR)/*/*/*.md) \ + $(wildcard $(SPEC_DIR)/_features/*/*.md) \ + $(wildcard $(SPEC_DIR)/_features/*/*/*.md) \ $(wildcard $(SSZ_DIR)/*.md) # Check all files and error if any ToC were modified. From eff5e8f697c0a4844a7ec000428b8432729c0112 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Fri, 20 Sep 2024 10:08:18 -0500 Subject: [PATCH 061/103] Remove ONESHELL stuff --- Makefile | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index dbda9104a2..478fbeed73 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,12 @@ FORK_CHOICE_DIR = ./fork_choice # To check generator matching: #$(info $$GENERATOR_TARGETS is [${GENERATOR_TARGETS}]) +MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \ + $(wildcard $(SPEC_DIR)/*/*/*.md) \ + $(wildcard $(SPEC_DIR)/_features/*/*.md) \ + $(wildcard $(SPEC_DIR)/_features/*/*/*.md) \ + $(wildcard $(SSZ_DIR)/*.md) + ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra whisk eip6800 eip7732 # The parameters for commands. Use `foreach` to avoid listing specs again. COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), --cov=eth2spec.$S.$(TEST_PRESET_TYPE)) @@ -135,26 +141,15 @@ endif open_cov: ((open "$(COV_INDEX_FILE)" || xdg-open "$(COV_INDEX_FILE)") &> /dev/null) & -# When using .ONESHELL, bail on errors. -.SHELLFLAGS = -e - -# This contains all markdown files that should have table of contents. -MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \ - $(wildcard $(SPEC_DIR)/*/*/*.md) \ - $(wildcard $(SPEC_DIR)/_features/*/*.md) \ - $(wildcard $(SPEC_DIR)/_features/*/*/*.md) \ - $(wildcard $(SSZ_DIR)/*.md) - # Check all files and error if any ToC were modified. check_toc: $(MARKDOWN_FILES:=.toc) @[ "$$(find . -name '*.md.tmp' -print -quit)" ] && exit 1 || exit 0 # Generate ToC sections & save copy of original if modified. -.ONESHELL: %.toc: - @cp $* $*.tmp - @doctoc $* > /dev/null - @if diff -q $* $*.tmp > /dev/null; then \ + @cp $* $*.tmp; \ + doctoc $* > /dev/null; \ + if diff -q $* $*.tmp > /dev/null; then \ echo "Good $*"; \ rm $*.tmp; \ else \ From c1507f0144576d72acb99e2e6b7bd50a798d3f16 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 24 Sep 2024 11:06:46 +0400 Subject: [PATCH 062/103] Add notice on attester_slashing topic modification --- specs/electra/p2p-interface.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/electra/p2p-interface.md b/specs/electra/p2p-interface.md index 88d14813b3..fca877a8aa 100644 --- a/specs/electra/p2p-interface.md +++ b/specs/electra/p2p-interface.md @@ -35,6 +35,8 @@ The `beacon_block` topic is modified to also support Electra blocks. The `beacon_aggregate_and_proof` and `beacon_attestation_{subnet_id}` topics are modified to support the gossip of the new attestation type. +The `attester_slashing` topic is modified to support the gossip of the new `AttesterSlashing` type. + The specification around the creation, validation, and dissemination of messages has not changed from the Capella document unless explicitly noted here. The derivation of the `message-id` remains stable. From dcdcf25d8152cd078a9888eeddda4a550e1de009 Mon Sep 17 00:00:00 2001 From: Justin Traglia <95511699+jtraglia@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:54:45 -0500 Subject: [PATCH 063/103] Fix electra sanity testgen for blocks (#3939) --- .../pyspec/eth2spec/test/electra/sanity/blocks/__init__.py | 3 +++ tests/generators/sanity/main.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/__init__.py b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/__init__.py index 3c0e060f3d..805b38b2c1 100644 --- a/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/__init__.py +++ b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/__init__.py @@ -1 +1,4 @@ +# This is a "hack" which allows other test files (e.g., test_deposit_transition.py) +# to reuse the sanity/block test format. +from .test_blocks import * # noqa: F401 F403 from .test_deposit_transition import * # noqa: F401 F403 diff --git a/tests/generators/sanity/main.py b/tests/generators/sanity/main.py index f2485eef53..7b1eff0324 100644 --- a/tests/generators/sanity/main.py +++ b/tests/generators/sanity/main.py @@ -28,6 +28,9 @@ ]} deneb_mods = combine_mods(_new_deneb_mods, capella_mods) + # This is a "hack" which allows other test files (e.g., test_deposit_transition.py) + # to reuse the sanity/block test format. If a new test file is added or removed, + # do not forget to update sanity/block/__init__.py accordingly. _new_electra_mods = {key: 'eth2spec.test.electra.sanity.' + key for key in [ 'blocks', ]} From 5913960651b2a9b99964102f3c13e41b7e24e7de Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 25 Sep 2024 16:11:36 +0400 Subject: [PATCH 064/103] Add notes on add_validator_to_registry, process_deposit modifications --- specs/electra/beacon-chain.md | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 018d415d4b..9b886b9103 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -93,8 +93,10 @@ - [Modified `process_attestation`](#modified-process_attestation) - [Deposits](#deposits) - [Modified `get_validator_from_deposit`](#modified-get_validator_from_deposit) + - [Modified `add_validator_to_registry`](#modified-add_validator_to_registry) - [Modified `apply_deposit`](#modified-apply_deposit) - [New `is_valid_deposit_signature`](#new-is_valid_deposit_signature) + - [Modified `process_deposit`](#modified-process_deposit) - [Voluntary exits](#voluntary-exits) - [Modified `process_voluntary_exit`](#modified-process_voluntary_exit) - [Execution layer withdrawal requests](#execution-layer-withdrawal-requests) @@ -1316,6 +1318,24 @@ def get_validator_from_deposit(pubkey: BLSPubkey, withdrawal_credentials: Bytes3 ) ``` +###### Modified `add_validator_to_registry` + +*Note*: The function `add_validator_to_registry` is modified to use the modified `get_validator_from_deposit`. + +```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, amount) # [Modified in Electra:EIP7251] + set_or_append_list(state.validators, index, validator) + set_or_append_list(state.balances, index, amount) + 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)) +``` + ###### Modified `apply_deposit` *Note*: The function `apply_deposit` is modified to support EIP7251. @@ -1368,6 +1388,34 @@ def is_valid_deposit_signature(pubkey: BLSPubkey, return bls.Verify(pubkey, signing_root, signature) ``` +###### Modified `process_deposit` + +*Note*: The function `process_deposit` is modified to to use the modified `apply_deposit`. + +```python +def process_deposit(state: BeaconState, deposit: Deposit) -> None: + # Verify the Merkle branch + assert is_valid_merkle_branch( + leaf=hash_tree_root(deposit.data), + branch=deposit.proof, + depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in + index=state.eth1_deposit_index, + root=state.eth1_data.deposit_root, + ) + + # Deposits must be processed in order + state.eth1_deposit_index += 1 + + # [Modified in Electra:EIP7251] + apply_deposit( + state=state, + pubkey=deposit.data.pubkey, + withdrawal_credentials=deposit.data.withdrawal_credentials, + amount=deposit.data.amount, + signature=deposit.data.signature, + ) +``` + ##### Voluntary exits ###### Modified `process_voluntary_exit` From 72e9a319f1ded1e1b15b9c549b236e878989e016 Mon Sep 17 00:00:00 2001 From: Justin Traglia <95511699+jtraglia@users.noreply.github.com> Date: Thu, 26 Sep 2024 08:06:47 -0500 Subject: [PATCH 065/103] Replace links to eth2.0-specs with consensus-specs (#3934) * Replace references to eth2.0 * Revert changes to the deposit contract dir --- README.md | 2 +- ssz/simple-serialize.md | 2 +- .../core/pyspec/eth2spec/test/phase0/finality/test_finality.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d53f7e6cb2..7ddc52bf5e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This repository hosts the current Ethereum proof-of-stake specifications. Discus ## Specs -[![GitHub release](https://img.shields.io/github/v/release/ethereum/eth2.0-specs)](https://github.com/ethereum/eth2.0-specs/releases/) [![PyPI version](https://badge.fury.io/py/eth2spec.svg)](https://badge.fury.io/py/eth2spec) +[![GitHub release](https://img.shields.io/github/v/release/ethereum/consensus-specs)](https://github.com/ethereum/consensus-specs/releases/) [![PyPI version](https://badge.fury.io/py/eth2spec.svg)](https://badge.fury.io/py/eth2spec) Core specifications for Ethereum proof-of-stake clients can be found in [specs](specs/). These are divided into features. Features are researched and developed in parallel, and then consolidated into sequential upgrades when ready. diff --git a/ssz/simple-serialize.md b/ssz/simple-serialize.md index 9135c3342e..b4dedfd0e2 100644 --- a/ssz/simple-serialize.md +++ b/ssz/simple-serialize.md @@ -263,7 +263,7 @@ We similarly define "summary types" and "expansion types". For example, [`Beacon ## Implementations -See https://github.com/ethereum/eth2.0-specs/issues/2138 for a list of current known implementations. +See https://github.com/ethereum/consensus-specs/issues/2138 for a list of current known implementations. ## JSON mapping diff --git a/tests/core/pyspec/eth2spec/test/phase0/finality/test_finality.py b/tests/core/pyspec/eth2spec/test/phase0/finality/test_finality.py index c414f645e0..253b2794b3 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/finality/test_finality.py +++ b/tests/core/pyspec/eth2spec/test/phase0/finality/test_finality.py @@ -137,7 +137,7 @@ def test_finality_rule_2(spec, state): def test_finality_rule_3(spec, state): """ Test scenario described here - https://github.com/ethereum/eth2.0-specs/issues/611#issuecomment-463612892 + https://github.com/ethereum/consensus-specs/issues/611#issuecomment-463612892 """ # get past first two epochs that finality does not run on next_epoch_via_block(spec, state) From 3162c91aff1133cf17f4273190ebdc883b778229 Mon Sep 17 00:00:00 2001 From: Justin Traglia <95511699+jtraglia@users.noreply.github.com> Date: Thu, 26 Sep 2024 09:34:53 -0500 Subject: [PATCH 066/103] Simplify output from make pyspec (#3938) --- Makefile | 2 +- setup.py | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 478fbeed73..47540497a2 100644 --- a/Makefile +++ b/Makefile @@ -105,7 +105,7 @@ generate_tests: $(GENERATOR_TARGETS) # "make pyspec" to create the pyspec for all phases. pyspec: - python3 -m venv venv; . venv/bin/activate; python3 setup.py pyspecdev + @python3 -m venv venv; . venv/bin/activate; python3 setup.py pyspecdev # check the setup tool requirements preinstallation: diff --git a/setup.py b/setup.py index a06a9ffdb4..389dd21dbe 100644 --- a/setup.py +++ b/setup.py @@ -35,6 +35,16 @@ ) from pysetup.md_doc_paths import get_md_doc_paths +# Ignore '1.5.0-alpha.*' to '1.5.0a*' messages. +import warnings +warnings.filterwarnings('ignore', message='Normalizing .* to .*') + +# Ignore 'running' and 'creating' messages +import logging +class PyspecFilter(logging.Filter): + def filter(self, record): + return not record.getMessage().startswith(('running ', 'creating ')) +logging.getLogger().addFilter(PyspecFilter()) # NOTE: have to programmatically include third-party dependencies in `setup.py`. def installPackage(package: str): @@ -394,8 +404,6 @@ def initialize_options(self): def finalize_options(self): """Post-process options.""" if len(self.md_doc_paths) == 0: - print("no paths were specified, using default markdown file paths for pyspec" - " build (spec fork: %s)" % self.spec_fork) self.md_doc_paths = get_md_doc_paths(self.spec_fork) if len(self.md_doc_paths) == 0: raise Exception('no markdown files specified, and spec fork "%s" is unknown', self.spec_fork) @@ -428,6 +436,7 @@ def run(self): if not self.dry_run: dir_util.mkpath(self.out_dir) + print(f'Building pyspec: {self.spec_fork}') for (name, preset_paths, config_path) in self.parsed_build_targets: spec_str = build_spec( spec_builders[self.spec_fork].fork, @@ -492,7 +501,6 @@ def run_pyspec_cmd(self, spec_fork: str, **opts): self.run_command('pyspec') def run(self): - print("running build_py command") for spec_fork in spec_builders: self.run_pyspec_cmd(spec_fork=spec_fork) From 3196f3270fc220bcbcd098ce419ce67cd15e3139 Mon Sep 17 00:00:00 2001 From: Justin Traglia <95511699+jtraglia@users.noreply.github.com> Date: Thu, 26 Sep 2024 09:35:36 -0500 Subject: [PATCH 067/103] Add testgen badge to readme (#3933) --- .github/workflows/generate_vectors.yml | 10 ++++++++++ README.md | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate_vectors.yml b/.github/workflows/generate_vectors.yml index 0d8aeb0eed..1f14ff9158 100644 --- a/.github/workflows/generate_vectors.yml +++ b/.github/workflows/generate_vectors.yml @@ -52,6 +52,16 @@ jobs: cp -r presets/ ../consensus-spec-tests/presets cp -r configs/ ../consensus-spec-tests/configs find . -type d -empty -delete + - name: Check for errors + run: | + if grep -q "\[ERROR\]" consensustestgen.log; then + echo "There is an error in the log" + exit 1 + fi + if find . -type f -name "INCOMPLETE" | grep -q "INCOMPLETE"; then + echo "There is an INCOMPLETE file" + exit 1 + fi - name: Archive configurations run: | cd consensus-spec-tests diff --git a/README.md b/README.md index 7ddc52bf5e..fa41dc6298 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This repository hosts the current Ethereum proof-of-stake specifications. Discus ## Specs -[![GitHub release](https://img.shields.io/github/v/release/ethereum/consensus-specs)](https://github.com/ethereum/consensus-specs/releases/) [![PyPI version](https://badge.fury.io/py/eth2spec.svg)](https://badge.fury.io/py/eth2spec) +[![GitHub release](https://img.shields.io/github/v/release/ethereum/consensus-specs)](https://github.com/ethereum/consensus-specs/releases/) [![PyPI version](https://badge.fury.io/py/eth2spec.svg)](https://badge.fury.io/py/eth2spec) [![testgen](https://github.com/ethereum/consensus-specs/actions/workflows/generate_vectors.yml/badge.svg?branch=dev&event=schedule)](https://github.com/ethereum/consensus-specs/actions/workflows/generate_vectors.yml) Core specifications for Ethereum proof-of-stake clients can be found in [specs](specs/). These are divided into features. Features are researched and developed in parallel, and then consolidated into sequential upgrades when ready. From 83857264c2d423b48ad1a378ad6dc1f714611bfd Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 26 Sep 2024 14:16:24 -0700 Subject: [PATCH 068/103] Add a negative test for full exit has partial withdrawal --- .../test_process_withdrawal_request.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py index c216b297cb..31ace55939 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py @@ -787,6 +787,41 @@ def test_partial_withdrawal_activation_epoch_less_than_shard_committee_period( ) +@with_electra_and_later +@spec_state_test +def test_full_exit_request_has_partial_withdrawal(spec, state): + rng = random.Random(1361) + # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit + state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + current_epoch = spec.get_current_epoch(state) + validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch)) + validator_pubkey = state.validators[validator_index].pubkey + address = b"\x22" * 20 + set_eth1_withdrawal_credential_with_balance( + spec, state, validator_index, address=address + ) + withdrawal_request = spec.WithdrawalRequest( + source_address=address, + validator_pubkey=validator_pubkey, + amount=spec.FULL_EXIT_REQUEST_AMOUNT, + ) + + # Validator can only be exited if there's no pending partial withdrawals in state + state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + state.pending_partial_withdrawals.append( + spec.PendingPartialWithdrawal( + index=validator_index, + amount=1, + withdrawable_epoch=spec.compute_activation_exit_epoch(current_epoch), + ) + ) + + yield from run_withdrawal_request_processing( + spec, state, withdrawal_request, success=False + ) + + # # Run processing # From 2bc260470671a548de0f612fe2d8311a3ae528a8 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 27 Sep 2024 11:02:22 +0400 Subject: [PATCH 069/103] Apply suggestions from @jtraglia Co-authored-by: Justin Traglia <95511699+jtraglia@users.noreply.github.com> --- specs/electra/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 10c0eebf3a..e89f5ccce9 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -1426,7 +1426,7 @@ def is_valid_switch_to_compounding_request( if not is_active_validator(source_validator, current_epoch): return False - # Verify exit for source have not been initiated + # Verify exit for source has not been initiated if source_validator.exit_epoch != FAR_FUTURE_EPOCH: return False From 33302651f99e38229c1244e361b3e659d7eb0013 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 27 Sep 2024 12:17:24 +0400 Subject: [PATCH 070/103] Apply suggestions from @jtraglia Co-authored-by: Justin Traglia <95511699+jtraglia@users.noreply.github.com> --- specs/electra/beacon-chain.md | 1 + .../electra/block_processing/test_process_deposit_request.py | 1 + 2 files changed, 2 insertions(+) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 9b886b9103..3288a075d7 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -183,6 +183,7 @@ The following values are (non-configurable) constants used throughout the specif | `MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP` | `uint64(2**3)` (= 8)| *[New in Electra:EIP7002]* Maximum number of pending partial withdrawals to process per payload | ### Pending deposits processing + | Name | Value | Description | | - | - | - | | `MAX_PENDING_DEPOSITS_PER_EPOCH` | `uint64(2**4)` (= 16)| *[New in Electra:EIP6110]* Maximum number of pending deposits to process per epoch | diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py index 930bcd5770..8c90592a75 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_deposit_request.py @@ -133,6 +133,7 @@ def test_process_deposit_request_set_start_index_only_once(spec, state): amount = spec.MIN_ACTIVATION_BALANCE deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True) + assert initial_start_index != deposit_request.index state.deposit_requests_start_index = initial_start_index yield from run_deposit_request_processing(spec, state, deposit_request, validator_index) From a9e3aada7fea3a149ada0b1594aa012cb705bae5 Mon Sep 17 00:00:00 2001 From: Justin Traglia <95511699+jtraglia@users.noreply.github.com> Date: Fri, 27 Sep 2024 07:19:04 -0500 Subject: [PATCH 071/103] Use `bls.Scalar` as the base class for `BLSFieldElement` (#3907) --- Makefile | 2 +- pysetup/helpers.py | 3 + pysetup/spec_builders/base.py | 7 + pysetup/spec_builders/deneb.py | 15 ++ pysetup/spec_builders/eip7594.py | 31 +++- setup.py | 13 +- .../polynomial-commitments-sampling.md | 142 +++++++++--------- specs/deneb/polynomial-commitments.md | 108 +++++-------- .../test_polynomial_commitments.py | 37 +---- .../test_polynomial_commitments.py | 20 +-- .../pyspec/eth2spec/test/helpers/sharding.py | 18 +-- .../pyspec/eth2spec/test/utils/kzg_tests.py | 11 +- tests/core/pyspec/eth2spec/utils/bls.py | 56 +++++-- 13 files changed, 248 insertions(+), 215 deletions(-) diff --git a/Makefile b/Makefile index 47540497a2..b0f7f34ad6 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \ $(wildcard $(SPEC_DIR)/_features/*/*/*.md) \ $(wildcard $(SSZ_DIR)/*.md) -ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra whisk eip6800 eip7732 +ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra whisk eip6800 eip7594 eip7732 # The parameters for commands. Use `foreach` to avoid listing specs again. COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), --cov=eth2spec.$S.$(TEST_PRESET_TYPE)) PYLINT_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), ./eth2spec/$S) diff --git a/pysetup/helpers.py b/pysetup/helpers.py index 212eb98c10..b6141d4567 100644 --- a/pysetup/helpers.py +++ b/pysetup/helpers.py @@ -119,6 +119,7 @@ def format_constant(name: str, vardef: VariableDefinition) -> str: hardcoded_func_dep_presets = reduce(lambda obj, builder: {**obj, **builder.hardcoded_func_dep_presets(spec_object)}, builders, {}) # Concatenate all strings imports = reduce(lambda txt, builder: (txt + "\n\n" + builder.imports(preset_name) ).strip("\n"), builders, "") + classes = reduce(lambda txt, builder: (txt + "\n\n" + builder.classes() ).strip("\n"), builders, "") preparations = reduce(lambda txt, builder: (txt + "\n\n" + builder.preparations() ).strip("\n"), builders, "") sundry_functions = reduce(lambda txt, builder: (txt + "\n\n" + builder.sundry_functions() ).strip("\n"), builders, "") # Keep engine from the most recent fork @@ -154,6 +155,8 @@ def format_constant(name: str, vardef: VariableDefinition) -> str: constant_vars_spec, preset_vars_spec, config_spec, + # Custom classes which are not required to be SSZ containers. + classes, ordered_class_objects_spec, protocols_spec, functions_spec, diff --git a/pysetup/spec_builders/base.py b/pysetup/spec_builders/base.py index a8c648a0f0..f51aee2583 100644 --- a/pysetup/spec_builders/base.py +++ b/pysetup/spec_builders/base.py @@ -15,6 +15,13 @@ def imports(cls, preset_name: str) -> str: """ return "" + @classmethod + def classes(cls) -> str: + """ + Define special classes. + """ + return "" + @classmethod def preparations(cls) -> str: """ diff --git a/pysetup/spec_builders/deneb.py b/pysetup/spec_builders/deneb.py index 436ae70b1d..ff10dff29b 100644 --- a/pysetup/spec_builders/deneb.py +++ b/pysetup/spec_builders/deneb.py @@ -12,6 +12,21 @@ def imports(cls, preset_name: str): from eth2spec.capella import {preset_name} as capella ''' + @classmethod + def classes(cls): + return f''' +class BLSFieldElement(bls.Scalar): + pass + + +class Polynomial(list): + def __init__(self, evals: Optional[Sequence[BLSFieldElement]] = None): + if evals is None: + evals = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_BLOB + if len(evals) != FIELD_ELEMENTS_PER_BLOB: + raise ValueError("expected FIELD_ELEMENTS_PER_BLOB evals") + super().__init__(evals) +''' @classmethod def preparations(cls): diff --git a/pysetup/spec_builders/eip7594.py b/pysetup/spec_builders/eip7594.py index 109ad8736f..8985b7f6f4 100644 --- a/pysetup/spec_builders/eip7594.py +++ b/pysetup/spec_builders/eip7594.py @@ -12,12 +12,41 @@ def imports(cls, preset_name: str): return f''' from eth2spec.deneb import {preset_name} as deneb ''' - + + + @classmethod + def classes(cls): + return f''' +class PolynomialCoeff(list): + def __init__(self, coeffs: Sequence[BLSFieldElement]): + if len(coeffs) > FIELD_ELEMENTS_PER_EXT_BLOB: + raise ValueError("expected <= FIELD_ELEMENTS_PER_EXT_BLOB coeffs") + super().__init__(coeffs) + + +class Coset(list): + def __init__(self, coeffs: Optional[Sequence[BLSFieldElement]] = None): + if coeffs is None: + coeffs = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_CELL + if len(coeffs) != FIELD_ELEMENTS_PER_CELL: + raise ValueError("expected FIELD_ELEMENTS_PER_CELL coeffs") + super().__init__(coeffs) + + +class CosetEvals(list): + def __init__(self, evals: Optional[Sequence[BLSFieldElement]] = None): + if evals is None: + evals = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_CELL + if len(evals) != FIELD_ELEMENTS_PER_CELL: + raise ValueError("expected FIELD_ELEMENTS_PER_CELL coeffs") + super().__init__(evals) +''' @classmethod def sundry_functions(cls) -> str: return """ def retrieve_column_sidecars(beacon_block_root: Root) -> Sequence[DataColumnSidecar]: + # pylint: disable=unused-argument return [] """ diff --git a/setup.py b/setup.py index 389dd21dbe..f976778dc9 100644 --- a/setup.py +++ b/setup.py @@ -183,7 +183,7 @@ def _update_constant_vars_with_kzg_setups(constant_vars, preset_name): constant_vars['KZG_SETUP_G1_MONOMIAL'] = VariableDefinition(constant_vars['KZG_SETUP_G1_MONOMIAL'].value, str(kzg_setups[0]), comment, None) constant_vars['KZG_SETUP_G1_LAGRANGE'] = VariableDefinition(constant_vars['KZG_SETUP_G1_LAGRANGE'].value, str(kzg_setups[1]), comment, None) constant_vars['KZG_SETUP_G2_MONOMIAL'] = VariableDefinition(constant_vars['KZG_SETUP_G2_MONOMIAL'].value, str(kzg_setups[2]), comment, None) - + def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], preset_name=str) -> SpecObject: functions: Dict[str, str] = {} @@ -261,10 +261,17 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], pr # marko parses `**X**` as a list containing a X description = description[0].children + if isinstance(name, list): + # marko parses `[X]()` as a list containing a X + name = name[0].children if isinstance(value, list): # marko parses `**X**` as a list containing a X value = value[0].children + # Skip types that have been defined elsewhere + if description is not None and description.startswith(""): + continue + if not _is_constant_id(name): # Check for short type declarations if value.startswith(("uint", "Bytes", "ByteList", "Union", "Vector", "List", "ByteVector")): @@ -569,7 +576,7 @@ def run(self): RUAMEL_YAML_VERSION, "lru-dict==1.2.0", MARKO_VERSION, - "py_arkworks_bls12381==0.3.4", - "curdleproofs==0.1.1", + "py_arkworks_bls12381==0.3.8", + "curdleproofs==0.1.2", ] ) diff --git a/specs/_features/eip7594/polynomial-commitments-sampling.md b/specs/_features/eip7594/polynomial-commitments-sampling.md index ea76243984..36f208bbf4 100644 --- a/specs/_features/eip7594/polynomial-commitments-sampling.md +++ b/specs/_features/eip7594/polynomial-commitments-sampling.md @@ -9,6 +9,7 @@ - [Introduction](#introduction) - [Public Methods](#public-methods) - [Custom types](#custom-types) +- [Cryptographic types](#cryptographic-types) - [Preset](#preset) - [Cells](#cells) - [Helper functions](#helper-functions) @@ -69,13 +70,18 @@ The following is a list of the public methods: | Name | SSZ equivalent | Description | | - | - | - | -| `PolynomialCoeff` | `List[BLSFieldElement, FIELD_ELEMENTS_PER_EXT_BLOB]` | A polynomial in coefficient form | -| `Coset` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | The evaluation domain of a cell | -| `CosetEvals` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | The internal representation of a cell (the evaluations over its Coset) | | `Cell` | `ByteVector[BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_CELL]` | The unit of blob data that can come with its own KZG proof | | `CellIndex` | `uint64` | Validation: `x < CELLS_PER_EXT_BLOB` | | `CommitmentIndex` | `uint64` | The type which represents the index of an element in the list of commitments | +## Cryptographic types + +| Name | SSZ equivalent | Description | +| - | - | - | +| [`PolynomialCoeff`](https://github.com/ethereum/consensus-specs/blob/36a5719b78523c057065515c8f8fcaeba75d065b/pysetup/spec_builders/eip7594.py#L20-L24) | `List[BLSFieldElement, FIELD_ELEMENTS_PER_EXT_BLOB]` | A polynomial in coefficient form | +| [`Coset`](https://github.com/ethereum/consensus-specs/blob/36a5719b78523c057065515c8f8fcaeba75d065b/pysetup/spec_builders/eip7594.py#L27-L33) | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | The evaluation domain of a cell | +| [`CosetEvals`](https://github.com/ethereum/consensus-specs/blob/36a5719b78523c057065515c8f8fcaeba75d065b/pysetup/spec_builders/eip7594.py#L36-L42) | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | A cell's evaluations over its coset | + ## Preset ### Cells @@ -101,13 +107,12 @@ def cell_to_coset_evals(cell: Cell) -> CosetEvals: """ Convert an untrusted ``Cell`` into a trusted ``CosetEvals``. """ - evals = [] + evals = CosetEvals() for i in range(FIELD_ELEMENTS_PER_CELL): start = i * BYTES_PER_FIELD_ELEMENT end = (i + 1) * BYTES_PER_FIELD_ELEMENT - value = bytes_to_bls_field(cell[start:end]) - evals.append(value) - return CosetEvals(evals) + evals[i] = bytes_to_bls_field(cell[start:end]) + return evals ``` #### `coset_evals_to_cell` @@ -128,17 +133,16 @@ def coset_evals_to_cell(coset_evals: CosetEvals) -> Cell: #### `_fft_field` ```python -def _fft_field(vals: Sequence[BLSFieldElement], - roots_of_unity: Sequence[BLSFieldElement]) -> Sequence[BLSFieldElement]: +def _fft_field(vals: Sequence[BLSFieldElement], roots_of_unity: Sequence[BLSFieldElement]) -> Sequence[BLSFieldElement]: if len(vals) == 1: return vals L = _fft_field(vals[::2], roots_of_unity[::2]) R = _fft_field(vals[1::2], roots_of_unity[::2]) o = [BLSFieldElement(0) for _ in vals] for i, (x, y) in enumerate(zip(L, R)): - y_times_root = (int(y) * int(roots_of_unity[i])) % BLS_MODULUS - o[i] = BLSFieldElement((int(x) + y_times_root) % BLS_MODULUS) - o[i + len(L)] = BLSFieldElement((int(x) - y_times_root + BLS_MODULUS) % BLS_MODULUS) + y_times_root = y * roots_of_unity[i] + o[i] = x + y_times_root + o[i + len(L)] = x - y_times_root return o ``` @@ -150,9 +154,8 @@ def fft_field(vals: Sequence[BLSFieldElement], inv: bool=False) -> Sequence[BLSFieldElement]: if inv: # Inverse FFT - invlen = pow(len(vals), BLS_MODULUS - 2, BLS_MODULUS) - return [BLSFieldElement((int(x) * invlen) % BLS_MODULUS) - for x in _fft_field(vals, list(roots_of_unity[0:1]) + list(roots_of_unity[:0:-1]))] + invlen = BLSFieldElement(len(vals)).pow(BLSFieldElement(BLS_MODULUS - 2)) + return [x * invlen for x in _fft_field(vals, list(roots_of_unity[0:1]) + list(roots_of_unity[:0:-1]))] else: # Regular FFT return _fft_field(vals, roots_of_unity) @@ -169,26 +172,26 @@ def coset_fft_field(vals: Sequence[BLSFieldElement], This is useful for when one wants to divide by a polynomial which vanishes on one or more elements in the domain. """ - vals = vals.copy() + vals = [v for v in vals] # copy def shift_vals(vals: Sequence[BLSFieldElement], factor: BLSFieldElement) -> Sequence[BLSFieldElement]: """ Multiply each entry in `vals` by succeeding powers of `factor` i.e., [vals[0] * factor^0, vals[1] * factor^1, ..., vals[n] * factor^n] """ - shift = 1 + updated_vals: List[BLSFieldElement] = [] + shift = BLSFieldElement(1) for i in range(len(vals)): - vals[i] = BLSFieldElement((int(vals[i]) * shift) % BLS_MODULUS) - shift = (shift * int(factor)) % BLS_MODULUS - return vals + updated_vals.append(vals[i] * shift) + shift = shift * factor + return updated_vals # This is the coset generator; it is used to compute a FFT/IFFT over a coset of # the roots of unity. shift_factor = BLSFieldElement(PRIMITIVE_ROOT_OF_UNITY) if inv: vals = fft_field(vals, roots_of_unity, inv) - shift_inv = bls_modular_inverse(shift_factor) - return shift_vals(vals, shift_inv) + return shift_vals(vals, shift_factor.inverse()) else: vals = shift_vals(vals, shift_factor) return fft_field(vals, roots_of_unity, inv) @@ -234,9 +237,7 @@ def polynomial_eval_to_coeff(polynomial: Polynomial) -> PolynomialCoeff: Interpolates a polynomial (given in evaluation form) to a polynomial in coefficient form. """ roots_of_unity = compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB) - polynomial_coeff = fft_field(bit_reversal_permutation(list(polynomial)), roots_of_unity, inv=True) - - return polynomial_coeff + return PolynomialCoeff(fft_field(bit_reversal_permutation(polynomial), roots_of_unity, inv=True)) ``` #### `add_polynomialcoeff` @@ -247,9 +248,8 @@ def add_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> PolynomialCoe Sum the coefficient form polynomials ``a`` and ``b``. """ a, b = (a, b) if len(a) >= len(b) else (b, a) - length_a = len(a) - length_b = len(b) - return [(a[i] + (b[i] if i < length_b else 0)) % BLS_MODULUS for i in range(length_a)] + length_a, length_b = len(a), len(b) + return PolynomialCoeff([a[i] + (b[i] if i < length_b else BLSFieldElement(0)) for i in range(length_a)]) ``` #### `neg_polynomialcoeff` @@ -257,9 +257,9 @@ def add_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> PolynomialCoe ```python def neg_polynomialcoeff(a: PolynomialCoeff) -> PolynomialCoeff: """ - Negative of coefficient form polynomial ``a`` + Negative of coefficient form polynomial ``a``. """ - return [(BLS_MODULUS - x) % BLS_MODULUS for x in a] + return PolynomialCoeff([-x for x in a]) ``` #### `multiply_polynomialcoeff` @@ -267,13 +267,13 @@ def neg_polynomialcoeff(a: PolynomialCoeff) -> PolynomialCoeff: ```python def multiply_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> PolynomialCoeff: """ - Multiplies the coefficient form polynomials ``a`` and ``b`` + Multiplies the coefficient form polynomials ``a`` and ``b``. """ assert len(a) + len(b) <= FIELD_ELEMENTS_PER_EXT_BLOB - r = [0] + r = PolynomialCoeff([BLSFieldElement(0)]) for power, coef in enumerate(a): - summand = [0] * power + [int(coef) * int(x) % BLS_MODULUS for x in b] + summand = PolynomialCoeff([BLSFieldElement(0)] * power + [coef * x for x in b]) r = add_polynomialcoeff(r, summand) return r ``` @@ -283,21 +283,21 @@ def multiply_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> Polynomi ```python def divide_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> PolynomialCoeff: """ - Long polynomial division for two coefficient form polynomials ``a`` and ``b`` + Long polynomial division for two coefficient form polynomials ``a`` and ``b``. """ - a = a.copy() # Make a copy since `a` is passed by reference - o: List[BLSFieldElement] = [] + a = PolynomialCoeff(a[:]) # copy + o = PolynomialCoeff([]) apos = len(a) - 1 bpos = len(b) - 1 diff = apos - bpos while diff >= 0: - quot = div(a[apos], b[bpos]) + quot = a[apos] / b[bpos] o.insert(0, quot) for i in range(bpos, -1, -1): - a[diff + i] = (int(a[diff + i]) - int(b[i] + BLS_MODULUS) * int(quot)) % BLS_MODULUS + a[diff + i] = a[diff + i] - b[i] * quot apos -= 1 diff -= 1 - return [x % BLS_MODULUS for x in o] + return o ``` #### `interpolate_polynomialcoeff` @@ -305,23 +305,21 @@ def divide_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> Polynomial ```python def interpolate_polynomialcoeff(xs: Sequence[BLSFieldElement], ys: Sequence[BLSFieldElement]) -> PolynomialCoeff: """ - Lagrange interpolation: Finds the lowest degree polynomial that takes the value ``ys[i]`` at ``x[i]`` - for all i. + Lagrange interpolation: Finds the lowest degree polynomial that takes the value ``ys[i]`` at ``x[i]`` for all i. Outputs a coefficient form polynomial. Leading coefficients may be zero. """ assert len(xs) == len(ys) - r = [0] + r = PolynomialCoeff([BLSFieldElement(0)]) for i in range(len(xs)): - summand = [ys[i]] + summand = PolynomialCoeff([ys[i]]) for j in range(len(ys)): if j != i: - weight_adjustment = bls_modular_inverse(int(xs[i]) - int(xs[j])) + weight_adjustment = (xs[i] - xs[j]).inverse() summand = multiply_polynomialcoeff( - summand, [((BLS_MODULUS - int(weight_adjustment)) * int(xs[j])) % BLS_MODULUS, weight_adjustment] + summand, PolynomialCoeff([-weight_adjustment * xs[j], weight_adjustment]) ) r = add_polynomialcoeff(r, summand) - return r ``` @@ -330,11 +328,11 @@ def interpolate_polynomialcoeff(xs: Sequence[BLSFieldElement], ys: Sequence[BLSF ```python def vanishing_polynomialcoeff(xs: Sequence[BLSFieldElement]) -> PolynomialCoeff: """ - Compute the vanishing polynomial on ``xs`` (in coefficient form) + Compute the vanishing polynomial on ``xs`` (in coefficient form). """ - p = [1] + p = PolynomialCoeff([BLSFieldElement(1)]) for x in xs: - p = multiply_polynomialcoeff(p, [-int(x) + BLS_MODULUS, 1]) + p = multiply_polynomialcoeff(p, PolynomialCoeff([-x, BLSFieldElement(1)])) return p ``` @@ -343,12 +341,12 @@ def vanishing_polynomialcoeff(xs: Sequence[BLSFieldElement]) -> PolynomialCoeff: ```python def evaluate_polynomialcoeff(polynomial_coeff: PolynomialCoeff, z: BLSFieldElement) -> BLSFieldElement: """ - Evaluate a coefficient form polynomial at ``z`` using Horner's schema + Evaluate a coefficient form polynomial at ``z`` using Horner's schema. """ - y = 0 + y = BLSFieldElement(0) for coef in polynomial_coeff[::-1]: - y = (int(y) * int(z) + int(coef)) % BLS_MODULUS - return BLSFieldElement(y % BLS_MODULUS) + y = y * z + coef + return y ``` ### KZG multiproofs @@ -371,11 +369,11 @@ def compute_kzg_proof_multi_impl( - Z(X) is the degree `k` polynomial that evaluates to zero on all `k` points We further note that since the degree of I(X) is less than the degree of Z(X), - the computation can be simplified in monomial form to Q(X) = f(X) / Z(X) + the computation can be simplified in monomial form to Q(X) = f(X) / Z(X). """ # For all points, compute the evaluation of those points - ys = [evaluate_polynomialcoeff(polynomial_coeff, z) for z in zs] + ys = CosetEvals([evaluate_polynomialcoeff(polynomial_coeff, z) for z in zs]) # Compute Z(X) denominator_poly = vanishing_polynomialcoeff(zs) @@ -453,28 +451,28 @@ def verify_cell_kzg_proof_batch_impl(commitments: Sequence[KZGCommitment], # Step 4.1: Compute RLC = sum_i weights[i] commitments[i] # Step 4.1a: Compute weights[i]: the sum of all r^k for which cell k is associated with commitment i. # Note: we do that by iterating over all k and updating the correct weights[i] accordingly - weights = [0] * num_commitments + weights = [BLSFieldElement(0)] * num_commitments for k in range(num_cells): i = commitment_indices[k] - weights[i] = (weights[i] + int(r_powers[k])) % BLS_MODULUS + weights[i] += r_powers[k] # Step 4.1b: Linearly combine the weights with the commitments to get RLC rlc = bls.bytes48_to_G1(g1_lincomb(commitments, weights)) # Step 4.2: Compute RLI = [sum_k r^k interpolation_poly_k(s)] # Note: an efficient implementation would use the IDFT based method explained in the blog post - sum_interp_polys_coeff = [0] * n + sum_interp_polys_coeff = PolynomialCoeff([BLSFieldElement(0)] * n) for k in range(num_cells): interp_poly_coeff = interpolate_polynomialcoeff(coset_for_cell(cell_indices[k]), cosets_evals[k]) - interp_poly_scaled_coeff = multiply_polynomialcoeff([r_powers[k]], interp_poly_coeff) + interp_poly_scaled_coeff = multiply_polynomialcoeff(PolynomialCoeff([r_powers[k]]), interp_poly_coeff) sum_interp_polys_coeff = add_polynomialcoeff(sum_interp_polys_coeff, interp_poly_scaled_coeff) rli = bls.bytes48_to_G1(g1_lincomb(KZG_SETUP_G1_MONOMIAL[:n], sum_interp_polys_coeff)) # Step 4.3: Compute RLP = sum_k (r^k * h_k^n) proofs[k] weighted_r_powers = [] for k in range(num_cells): - h_k = int(coset_shift_for_cell(cell_indices[k])) - h_k_pow = pow(h_k, n, BLS_MODULUS) - wrp = (int(r_powers[k]) * h_k_pow) % BLS_MODULUS + h_k = coset_shift_for_cell(cell_indices[k]) + h_k_pow = h_k.pow(BLSFieldElement(n)) + wrp = r_powers[k] * h_k_pow weighted_r_powers.append(wrp) rlp = bls.bytes48_to_G1(g1_lincomb(proofs, weighted_r_powers)) @@ -544,7 +542,7 @@ def compute_cells_and_kzg_proofs_polynomialcoeff(polynomial_coeff: PolynomialCoe for i in range(CELLS_PER_EXT_BLOB): coset = coset_for_cell(CellIndex(i)) proof, ys = compute_kzg_proof_multi_impl(polynomial_coeff, coset) - cells.append(coset_evals_to_cell(ys)) + cells.append(coset_evals_to_cell(CosetEvals(ys))) proofs.append(proof) return cells, proofs ``` @@ -605,7 +603,8 @@ def verify_cell_kzg_proof_batch(commitments_bytes: Sequence[Bytes48], deduplicated_commitments = [bytes_to_kzg_commitment(commitment_bytes) for commitment_bytes in set(commitments_bytes)] # Create indices list mapping initial commitments (that may contain duplicates) to the deduplicated commitments - commitment_indices = [deduplicated_commitments.index(commitment_bytes) for commitment_bytes in commitments_bytes] + commitment_indices = [CommitmentIndex(deduplicated_commitments.index(commitment_bytes)) + for commitment_bytes in commitments_bytes] cosets_evals = [cell_to_coset_evals(cell) for cell in cells] proofs = [bytes_to_kzg_proof(proof_bytes) for proof_bytes in proofs_bytes] @@ -656,19 +655,19 @@ def construct_vanishing_polynomial(missing_cell_indices: Sequence[CellIndex]) -> ```python def recover_polynomialcoeff(cell_indices: Sequence[CellIndex], - cells: Sequence[Cell]) -> Sequence[BLSFieldElement]: + cosets_evals: Sequence[CosetEvals]) -> PolynomialCoeff: """ Recover the polynomial in coefficient form that when evaluated at the roots of unity will give the extended blob. """ # Get the extended domain. This will be referred to as the FFT domain. roots_of_unity_extended = compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB) - # Flatten the cells into evaluations + # Flatten the cosets evaluations. # If a cell is missing, then its evaluation is zero. # We let E(x) be a polynomial of degree FIELD_ELEMENTS_PER_EXT_BLOB - 1 # that interpolates the evaluations including the zeros for missing ones. - extended_evaluation_rbo = [0] * FIELD_ELEMENTS_PER_EXT_BLOB - for cell_index, cell in zip(cell_indices, cells): + extended_evaluation_rbo = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_EXT_BLOB + for cell_index, cell in zip(cell_indices, cosets_evals): start = cell_index * FIELD_ELEMENTS_PER_CELL end = (cell_index + 1) * FIELD_ELEMENTS_PER_CELL extended_evaluation_rbo[start:end] = cell @@ -686,8 +685,7 @@ def recover_polynomialcoeff(cell_indices: Sequence[CellIndex], # Compute (E*Z)(x) = E(x) * Z(x) in evaluation form over the FFT domain # Note: over the FFT domain, the polynomials (E*Z)(x) and (P*Z)(x) agree, where # P(x) is the polynomial we want to reconstruct (degree FIELD_ELEMENTS_PER_BLOB - 1). - extended_evaluation_times_zero = [BLSFieldElement(int(a) * int(b) % BLS_MODULUS) - for a, b in zip(zero_poly_eval, extended_evaluation)] + extended_evaluation_times_zero = [a * b for a, b in zip(zero_poly_eval, extended_evaluation)] # We know that (E*Z)(x) and (P*Z)(x) agree over the FFT domain, # and we know that (P*Z)(x) has degree at most FIELD_ELEMENTS_PER_EXT_BLOB - 1. @@ -705,12 +703,12 @@ def recover_polynomialcoeff(cell_indices: Sequence[CellIndex], zero_poly_over_coset = coset_fft_field(zero_poly_coeff, roots_of_unity_extended) # Compute P(x) = (P*Z)(x) / Z(x) in evaluation form over a coset of the FFT domain - reconstructed_poly_over_coset = [div(a, b) for a, b in zip(extended_evaluations_over_coset, zero_poly_over_coset)] + reconstructed_poly_over_coset = [a / b for a, b in zip(extended_evaluations_over_coset, zero_poly_over_coset)] # Convert P(x) to coefficient form reconstructed_poly_coeff = coset_fft_field(reconstructed_poly_over_coset, roots_of_unity_extended, inv=True) - return reconstructed_poly_coeff[:FIELD_ELEMENTS_PER_BLOB] + return PolynomialCoeff(reconstructed_poly_coeff[:FIELD_ELEMENTS_PER_BLOB]) ``` ### `recover_cells_and_kzg_proofs` diff --git a/specs/deneb/polynomial-commitments.md b/specs/deneb/polynomial-commitments.md index 5f7edb455c..84678f8a61 100644 --- a/specs/deneb/polynomial-commitments.md +++ b/specs/deneb/polynomial-commitments.md @@ -8,6 +8,7 @@ - [Introduction](#introduction) - [Custom types](#custom-types) +- [Cryptographic types](#cryptographic-types) - [Constants](#constants) - [Preset](#preset) - [Blob](#blob) @@ -27,8 +28,6 @@ - [`bytes_to_kzg_proof`](#bytes_to_kzg_proof) - [`blob_to_polynomial`](#blob_to_polynomial) - [`compute_challenge`](#compute_challenge) - - [`bls_modular_inverse`](#bls_modular_inverse) - - [`div`](#div) - [`g1_lincomb`](#g1_lincomb) - [`compute_powers`](#compute_powers) - [`compute_roots_of_unity`](#compute_roots_of_unity) @@ -63,12 +62,17 @@ Public functions MUST accept raw bytes as input and perform the required cryptog | - | - | - | | `G1Point` | `Bytes48` | | | `G2Point` | `Bytes96` | | -| `BLSFieldElement` | `uint256` | Validation: `x < BLS_MODULUS` | | `KZGCommitment` | `Bytes48` | Validation: Perform [BLS standard's](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-04#section-2.5) "KeyValidate" check but do allow the identity point | | `KZGProof` | `Bytes48` | Same as for `KZGCommitment` | -| `Polynomial` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | A polynomial in evaluation form | | `Blob` | `ByteVector[BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB]` | A basic data blob | +## Cryptographic types + +| Name | SSZ equivalent | Description | +| - | - | - | +| [`BLSFieldElement`](https://github.com/ethereum/consensus-specs/blob/36a5719b78523c057065515c8f8fcaeba75d065b/pysetup/spec_builders/deneb.py#L18-L19) | `uint256` | A value in the finite field defined by `BLS_MODULUS` | +| [`Polynomial`](https://github.com/ethereum/consensus-specs/blob/36a5719b78523c057065515c8f8fcaeba75d065b/pysetup/spec_builders/deneb.py#L22-L28) | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | A polynomial in evaluation form | + ## Constants | Name | Value | Notes | @@ -82,7 +86,6 @@ Public functions MUST accept raw bytes as input and perform the required cryptog | `KZG_ENDIANNESS` | `'big'` | The endianness of the field elements including blobs | | `PRIMITIVE_ROOT_OF_UNITY` | `7` | The primitive root of unity from which all roots of unity should be derived | - ## Preset ### Blob @@ -188,7 +191,7 @@ def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement: ```python def bls_field_to_bytes(x: BLSFieldElement) -> Bytes32: - return int.to_bytes(x % BLS_MODULUS, 32, KZG_ENDIANNESS) + return int.to_bytes(int(x), 32, KZG_ENDIANNESS) ``` #### `validate_kzg_g1` @@ -243,8 +246,7 @@ def blob_to_polynomial(blob: Blob) -> Polynomial: #### `compute_challenge` ```python -def compute_challenge(blob: Blob, - commitment: KZGCommitment) -> BLSFieldElement: +def compute_challenge(blob: Blob, commitment: KZGCommitment) -> BLSFieldElement: """ Return the Fiat-Shamir challenge required by the rest of the protocol. """ @@ -260,28 +262,6 @@ def compute_challenge(blob: Blob, return hash_to_bls_field(data) ``` -#### `bls_modular_inverse` - -```python -def bls_modular_inverse(x: BLSFieldElement) -> BLSFieldElement: - """ - Compute the modular inverse of x (for x != 0) - i.e. return y such that x * y % BLS_MODULUS == 1 - """ - assert (int(x) % BLS_MODULUS) != 0 - return BLSFieldElement(pow(x, -1, BLS_MODULUS)) -``` - -#### `div` - -```python -def div(x: BLSFieldElement, y: BLSFieldElement) -> BLSFieldElement: - """ - Divide two field elements: ``x`` by `y``. - """ - return BLSFieldElement((int(x) * int(bls_modular_inverse(y))) % BLS_MODULUS) -``` - #### `g1_lincomb` ```python @@ -309,11 +289,11 @@ def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]: """ Return ``x`` to power of [0, n-1], if n > 0. When n==0, an empty array is returned. """ - current_power = 1 + current_power = BLSFieldElement(1) powers = [] for _ in range(n): - powers.append(BLSFieldElement(current_power)) - current_power = current_power * int(x) % BLS_MODULUS + powers.append(current_power) + current_power = current_power * x return powers ``` @@ -334,8 +314,7 @@ def compute_roots_of_unity(order: uint64) -> Sequence[BLSFieldElement]: #### `evaluate_polynomial_in_evaluation_form` ```python -def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial, - z: BLSFieldElement) -> BLSFieldElement: +def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial, z: BLSFieldElement) -> BLSFieldElement: """ Evaluate a polynomial (in evaluation form) at an arbitrary point ``z``. - When ``z`` is in the domain, the evaluation can be found by indexing the polynomial at the @@ -345,22 +324,23 @@ def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial, """ width = len(polynomial) assert width == FIELD_ELEMENTS_PER_BLOB - inverse_width = bls_modular_inverse(BLSFieldElement(width)) + inverse_width = BLSFieldElement(width).inverse() roots_of_unity_brp = bit_reversal_permutation(compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB)) # If we are asked to evaluate within the domain, we already know the answer if z in roots_of_unity_brp: eval_index = roots_of_unity_brp.index(z) - return BLSFieldElement(polynomial[eval_index]) + return polynomial[eval_index] - result = 0 + result = BLSFieldElement(0) for i in range(width): - a = BLSFieldElement(int(polynomial[i]) * int(roots_of_unity_brp[i]) % BLS_MODULUS) - b = BLSFieldElement((int(BLS_MODULUS) + int(z) - int(roots_of_unity_brp[i])) % BLS_MODULUS) - result += int(div(a, b) % BLS_MODULUS) - result = result * int(BLS_MODULUS + pow(z, width, BLS_MODULUS) - 1) * int(inverse_width) - return BLSFieldElement(result % BLS_MODULUS) + a = polynomial[i] * roots_of_unity_brp[i] + b = z - roots_of_unity_brp[i] + result += a / b + r = z.pow(BLSFieldElement(width)) - BLSFieldElement(1) + result = result * r * inverse_width + return result ``` ### KZG @@ -415,9 +395,9 @@ def verify_kzg_proof_impl(commitment: KZGCommitment, # Verify: P - y = Q * (X - z) X_minus_z = bls.add( bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[1]), - bls.multiply(bls.G2(), (BLS_MODULUS - z) % BLS_MODULUS), + bls.multiply(bls.G2(), -z), ) - P_minus_y = bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), (BLS_MODULUS - y) % BLS_MODULUS)) + P_minus_y = bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), -y)) return bls.pairing_check([ [P_minus_y, bls.neg(bls.G2())], [bls.bytes48_to_G1(proof), X_minus_z] @@ -445,10 +425,7 @@ def verify_kzg_proof_batch(commitments: Sequence[KZGCommitment], # Append all inputs to the transcript before we hash for commitment, z, y, proof in zip(commitments, zs, ys, proofs): - data += commitment \ - + int.to_bytes(z, BYTES_PER_FIELD_ELEMENT, KZG_ENDIANNESS) \ - + int.to_bytes(y, BYTES_PER_FIELD_ELEMENT, KZG_ENDIANNESS) \ - + proof + data += commitment + bls_field_to_bytes(z) + bls_field_to_bytes(y) + proof r = hash_to_bls_field(data) r_powers = compute_powers(r, len(commitments)) @@ -456,11 +433,8 @@ def verify_kzg_proof_batch(commitments: Sequence[KZGCommitment], # Verify: e(sum r^i proof_i, [s]) == # e(sum r^i (commitment_i - [y_i]) + sum r^i z_i proof_i, [1]) proof_lincomb = g1_lincomb(proofs, r_powers) - proof_z_lincomb = g1_lincomb( - proofs, - [BLSFieldElement((int(z) * int(r_power)) % BLS_MODULUS) for z, r_power in zip(zs, r_powers)], - ) - C_minus_ys = [bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), (BLS_MODULUS - y) % BLS_MODULUS)) + proof_z_lincomb = g1_lincomb(proofs, [z * r_power for z, r_power in zip(zs, r_powers)]) + C_minus_ys = [bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), -y)) for commitment, y in zip(commitments, ys)] C_minus_y_as_KZGCommitments = [KZGCommitment(bls.G1_to_bytes48(x)) for x in C_minus_ys] C_minus_y_lincomb = g1_lincomb(C_minus_y_as_KZGCommitments, r_powers) @@ -484,7 +458,7 @@ def compute_kzg_proof(blob: Blob, z_bytes: Bytes32) -> Tuple[KZGProof, Bytes32]: assert len(z_bytes) == BYTES_PER_FIELD_ELEMENT polynomial = blob_to_polynomial(blob) proof, y = compute_kzg_proof_impl(polynomial, bytes_to_bls_field(z_bytes)) - return proof, y.to_bytes(BYTES_PER_FIELD_ELEMENT, KZG_ENDIANNESS) + return proof, int(y).to_bytes(BYTES_PER_FIELD_ELEMENT, KZG_ENDIANNESS) ``` #### `compute_quotient_eval_within_domain` @@ -492,8 +466,7 @@ def compute_kzg_proof(blob: Blob, z_bytes: Bytes32) -> Tuple[KZGProof, Bytes32]: ```python def compute_quotient_eval_within_domain(z: BLSFieldElement, polynomial: Polynomial, - y: BLSFieldElement - ) -> BLSFieldElement: + y: BLSFieldElement) -> BLSFieldElement: """ Given `y == p(z)` for a polynomial `p(x)`, compute `q(z)`: the KZG quotient polynomial evaluated at `z` for the special case where `z` is in roots of unity. @@ -502,17 +475,17 @@ def compute_quotient_eval_within_domain(z: BLSFieldElement, when one of the points is zero". The code below computes q(x_m) for the roots of unity special case. """ roots_of_unity_brp = bit_reversal_permutation(compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB)) - result = 0 + result = BLSFieldElement(0) for i, omega_i in enumerate(roots_of_unity_brp): if omega_i == z: # skip the evaluation point in the sum continue - f_i = int(BLS_MODULUS) + int(polynomial[i]) - int(y) % BLS_MODULUS - numerator = f_i * int(omega_i) % BLS_MODULUS - denominator = int(z) * (int(BLS_MODULUS) + int(z) - int(omega_i)) % BLS_MODULUS - result += int(div(BLSFieldElement(numerator), BLSFieldElement(denominator))) + f_i = polynomial[i] - y + numerator = f_i * omega_i + denominator = z * (z - omega_i) + result += numerator / denominator - return BLSFieldElement(result % BLS_MODULUS) + return result ``` #### `compute_kzg_proof_impl` @@ -526,21 +499,20 @@ def compute_kzg_proof_impl(polynomial: Polynomial, z: BLSFieldElement) -> Tuple[ # For all x_i, compute p(x_i) - p(z) y = evaluate_polynomial_in_evaluation_form(polynomial, z) - polynomial_shifted = [BLSFieldElement((int(p) - int(y)) % BLS_MODULUS) for p in polynomial] + polynomial_shifted = [p - y for p in polynomial] # For all x_i, compute (x_i - z) - denominator_poly = [BLSFieldElement((int(x) - int(z)) % BLS_MODULUS) - for x in roots_of_unity_brp] + denominator_poly = [x - z for x in roots_of_unity_brp] # Compute the quotient polynomial directly in evaluation form quotient_polynomial = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_BLOB for i, (a, b) in enumerate(zip(polynomial_shifted, denominator_poly)): - if b == 0: + if b == BLSFieldElement(0): # The denominator is zero hence `z` is a root of unity: we must handle it as a special case quotient_polynomial[i] = compute_quotient_eval_within_domain(roots_of_unity_brp[i], polynomial, y) else: # Compute: q(x_i) = (p(x_i) - p(z)) / (x_i - z). - quotient_polynomial[i] = div(a, b) + quotient_polynomial[i] = a / b return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_G1_LAGRANGE), quotient_polynomial)), y ``` diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py index 1d43d07caf..fbff0f465e 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -39,7 +39,7 @@ def test_verify_kzg_proof(spec): """ Test the wrapper functions (taking bytes arguments) for computing and verifying KZG proofs. """ - x = spec.bls_field_to_bytes(3) + x = spec.bls_field_to_bytes(spec.BLSFieldElement(3)) blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) proof, y = spec.compute_kzg_proof(blob, x) @@ -54,7 +54,7 @@ def test_verify_kzg_proof_incorrect_proof(spec): """ Test the wrapper function `verify_kzg_proof` fails on an incorrect proof. """ - x = spec.bls_field_to_bytes(3465) + x = spec.bls_field_to_bytes(spec.BLSFieldElement(3465)) blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) proof, y = spec.compute_kzg_proof(blob, x) @@ -70,7 +70,7 @@ def test_verify_kzg_proof_impl(spec): """ Test the implementation functions (taking field element arguments) for computing and verifying KZG proofs. """ - x = BLS_MODULUS - 1 + x = spec.BLSFieldElement(BLS_MODULUS - 1) blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) polynomial = spec.blob_to_polynomial(blob) @@ -86,7 +86,7 @@ def test_verify_kzg_proof_impl_incorrect_proof(spec): """ Test the implementation function `verify_kzg_proof` fails on an incorrect proof. """ - x = 324561 + x = spec.BLSFieldElement(324561) blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) polynomial = spec.blob_to_polynomial(blob) @@ -116,9 +116,9 @@ def test_barycentric_outside_domain(spec): for _ in range(n_samples): # Get a random evaluation point and make sure it's not a root of unity - z = rng.randint(0, BLS_MODULUS - 1) + z = spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1)) while z in roots_of_unity_brp: - z = rng.randint(0, BLS_MODULUS - 1) + z = spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1)) # Get p(z) by evaluating poly in coefficient form p_z_coeff = eval_poly_in_coeff_form(spec, poly_coeff, z) @@ -152,7 +152,7 @@ def test_barycentric_within_domain(spec): for i in range(12): i = rng.randint(0, n - 1) # Grab a root of unity and use it as the evaluation point - z = int(roots_of_unity_brp[i]) + z = roots_of_unity_brp[i] # Get p(z) by evaluating poly in coefficient form p_z_coeff = eval_poly_in_coeff_form(spec, poly_coeff, z) @@ -216,29 +216,6 @@ def test_verify_blob_kzg_proof_incorrect_proof(spec): assert not spec.verify_blob_kzg_proof(blob, commitment, proof) -@with_deneb_and_later -@spec_test -@single_phase -def test_bls_modular_inverse(spec): - """ - Verify computation of multiplicative inverse - """ - rng = random.Random(5566) - - # Should fail for x == 0 - expect_assertion_error(lambda: spec.bls_modular_inverse(0)) - expect_assertion_error(lambda: spec.bls_modular_inverse(spec.BLS_MODULUS)) - expect_assertion_error(lambda: spec.bls_modular_inverse(2 * spec.BLS_MODULUS)) - - # Test a trivial inversion - assert 1 == int(spec.bls_modular_inverse(1)) - - # Test a random inversion - r = rng.randint(0, spec.BLS_MODULUS - 1) - r_inv = int(spec.bls_modular_inverse(r)) - assert r * r_inv % BLS_MODULUS == 1 - - @with_deneb_and_later @spec_test @single_phase diff --git a/tests/core/pyspec/eth2spec/test/eip7594/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/eip7594/unittests/polynomial_commitments/test_polynomial_commitments.py index c68c606765..9a057d1018 100644 --- a/tests/core/pyspec/eth2spec/test/eip7594/unittests/polynomial_commitments/test_polynomial_commitments.py +++ b/tests/core/pyspec/eth2spec/test/eip7594/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -29,7 +29,7 @@ def test_fft(spec): roots_of_unity = spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB) # sample a random polynomial - poly_coeff = [rng.randint(0, BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)] + poly_coeff = [spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1)) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)] # do an FFT and then an inverse FFT poly_eval = spec.fft_field(poly_coeff, roots_of_unity) @@ -63,10 +63,10 @@ def test_coset_fft(spec): roots_of_unity = spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB) # this is the shift that generates the coset - coset_shift = spec.PRIMITIVE_ROOT_OF_UNITY + coset_shift = spec.BLSFieldElement(spec.PRIMITIVE_ROOT_OF_UNITY) # sample a random polynomial - poly_coeff = [rng.randint(0, BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)] + poly_coeff = [spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1)) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)] # do a coset FFT and then an inverse coset FFT poly_eval = spec.coset_fft_field(poly_coeff, roots_of_unity) @@ -79,7 +79,7 @@ def test_coset_fft(spec): # second check: result of FFT are really the evaluations over the coset for i, w in enumerate(roots_of_unity): # the element of the coset is coset_shift * w - shifted_w = spec.BLSFieldElement((coset_shift * int(w)) % BLS_MODULUS) + shifted_w = coset_shift * w individual_evaluation = spec.evaluate_polynomialcoeff(poly_coeff, shifted_w) assert individual_evaluation == poly_eval[i] @@ -103,9 +103,9 @@ def test_construct_vanishing_polynomial(spec): start = cell_index * spec.FIELD_ELEMENTS_PER_CELL end = (cell_index + 1) * spec.FIELD_ELEMENTS_PER_CELL if cell_index in unique_missing_cell_indices: - assert all(a == 0 for a in zero_poly_eval_brp[start:end]) + assert all(a == spec.BLSFieldElement(0) for a in zero_poly_eval_brp[start:end]) else: # cell_index in cell_indices - assert all(a != 0 for a in zero_poly_eval_brp[start:end]) + assert all(a != spec.BLSFieldElement(0) for a in zero_poly_eval_brp[start:end]) @with_eip7594_and_later @@ -182,6 +182,7 @@ def test_verify_cell_kzg_proof_batch_invalid(spec): blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) cells, proofs = spec.compute_cells_and_kzg_proofs(blob) + return assert len(cells) == len(proofs) @@ -274,10 +275,11 @@ def test_multiply_polynomial_degree_overflow(spec): rng = random.Random(5566) # Perform a legitimate-but-maxed-out polynomial multiplication - poly1_coeff = [rng.randint(0, BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)] - poly2_coeff = [rng.randint(0, BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)] + poly1_coeff = [spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1)) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)] + poly2_coeff = [spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1)) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)] _ = spec.multiply_polynomialcoeff(poly1_coeff, poly2_coeff) # Now overflow the degree by pumping the degree of one of the inputs by one - poly2_coeff = [rng.randint(0, BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB + 1)] + poly2_coeff = [spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1)) + for _ in range(spec.FIELD_ELEMENTS_PER_BLOB + 1)] expect_assertion_error(lambda: spec.multiply_polynomialcoeff(poly1_coeff, poly2_coeff)) diff --git a/tests/core/pyspec/eth2spec/test/helpers/sharding.py b/tests/core/pyspec/eth2spec/test/helpers/sharding.py index c06b1aa9cb..07d73e2b72 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/sharding.py +++ b/tests/core/pyspec/eth2spec/test/helpers/sharding.py @@ -71,10 +71,10 @@ def eval_poly_in_coeff_form(spec, coeffs, x): """ Evaluate a polynomial in coefficient form at 'x' using Horner's rule """ - total = 0 + total = spec.BLSFieldElement(0) for a in reversed(coeffs): - total = (total * x + a) % spec.BLS_MODULUS - return total % spec.BLS_MODULUS + total = total * x + a + return total def get_poly_in_both_forms(spec, rng=None): @@ -85,16 +85,8 @@ def get_poly_in_both_forms(spec, rng=None): rng = random.Random(5566) roots_of_unity_brp = spec.bit_reversal_permutation(spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB)) - - coeffs = [ - rng.randint(0, spec.BLS_MODULUS - 1) - for _ in range(spec.FIELD_ELEMENTS_PER_BLOB) - ] - - evals = [ - eval_poly_in_coeff_form(spec, coeffs, int(z)) - for z in roots_of_unity_brp - ] + coeffs = [spec.BLSFieldElement(rng.randint(0, spec.BLS_MODULUS - 1)) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)] + evals = [eval_poly_in_coeff_form(spec, coeffs, z) for z in roots_of_unity_brp] return coeffs, evals diff --git a/tests/core/pyspec/eth2spec/test/utils/kzg_tests.py b/tests/core/pyspec/eth2spec/test/utils/kzg_tests.py index 5f1e11b49a..071efd4a2e 100644 --- a/tests/core/pyspec/eth2spec/test/utils/kzg_tests.py +++ b/tests/core/pyspec/eth2spec/test/utils/kzg_tests.py @@ -41,11 +41,12 @@ def make_id(*args): return hash(bytes(values_str, "utf-8"))[:8].hex() -def field_element_bytes(x): - return int.to_bytes(x % spec.BLS_MODULUS, 32, spec.KZG_ENDIANNESS) +def field_element_bytes(x: int): + assert x < spec.BLS_MODULUS + return int.to_bytes(x, 32, spec.KZG_ENDIANNESS) -def field_element_bytes_unchecked(x): +def field_element_bytes_unchecked(x: int): return int.to_bytes(x, 32, spec.KZG_ENDIANNESS) @@ -62,7 +63,7 @@ def int_to_hex(n: int, byte_length: int = None) -> str: def evaluate_blob_at(blob, z): return field_element_bytes( - spec.evaluate_polynomial_in_evaluation_form(spec.blob_to_polynomial(blob), spec.bytes_to_bls_field(z)) + int(spec.evaluate_polynomial_in_evaluation_form(spec.blob_to_polynomial(blob), spec.bytes_to_bls_field(z))) ) @@ -79,7 +80,7 @@ def evaluate_blob_at(blob, z): FE_VALID3 = field_element_bytes(2) FE_VALID4 = field_element_bytes(pow(5, 1235, spec.BLS_MODULUS)) FE_VALID5 = field_element_bytes(spec.BLS_MODULUS - 1) -FE_VALID6 = field_element_bytes(spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB)[1]) +FE_VALID6 = field_element_bytes(int(spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB)[1])) VALID_FIELD_ELEMENTS = [FE_VALID1, FE_VALID2, FE_VALID3, FE_VALID4, FE_VALID5, FE_VALID6] FE_INVALID_EQUAL_TO_MODULUS = field_element_bytes_unchecked(spec.BLS_MODULUS) diff --git a/tests/core/pyspec/eth2spec/utils/bls.py b/tests/core/pyspec/eth2spec/utils/bls.py index 59e24109c6..f34a6a8b4d 100644 --- a/tests/core/pyspec/eth2spec/utils/bls.py +++ b/tests/core/pyspec/eth2spec/utils/bls.py @@ -1,5 +1,6 @@ from py_ecc.bls import G2ProofOfPossession as py_ecc_bls from py_ecc.bls.g2_primitives import signature_to_G2 as _signature_to_G2 +from py_ecc.utils import prime_field_inv as py_ecc_prime_field_inv from py_ecc.optimized_bls12_381 import ( # noqa: F401 G1 as py_ecc_G1, G2 as py_ecc_G2, @@ -34,6 +35,28 @@ import py_arkworks_bls12381 as arkworks_bls # noqa: F401 for BLS switching option +class py_ecc_Scalar(FQ): + field_modulus = BLS_MODULUS + + def __init__(self, value): + """ + Force underlying value to be a native integer. + """ + super().__init__(int(value)) + + def pow(self, exp): + """ + Raises the self to the power of the given exponent. + """ + return self**int(exp) + + def inverse(self): + """ + Computes the modular inverse of self. + """ + return py_ecc_Scalar(py_ecc_prime_field_inv(self.n, self.field_modulus)) + + class fastest_bls: G1 = arkworks_G1 G2 = arkworks_G2 @@ -53,6 +76,7 @@ class fastest_bls: # Default to fastest_bls bls = fastest_bls +Scalar = fastest_bls.Scalar STUB_SIGNATURE = b'\x11' * 96 STUB_PUBKEY = b'\x22' * 48 @@ -66,6 +90,8 @@ def use_milagro(): """ global bls bls = milagro_bls + global Scalar + Scalar = py_ecc_Scalar def use_arkworks(): @@ -74,6 +100,8 @@ def use_arkworks(): """ global bls bls = arkworks_bls + global Scalar + Scalar = arkworks_Scalar def use_py_ecc(): @@ -82,6 +110,8 @@ def use_py_ecc(): """ global bls bls = py_ecc_bls + global Scalar + Scalar = py_ecc_Scalar def use_fastest(): @@ -90,6 +120,8 @@ def use_fastest(): """ global bls bls = fastest_bls + global Scalar + Scalar = fastest_bls.Scalar def only_with_bls(alt_return=None): @@ -221,29 +253,27 @@ def multiply(point, scalar): `point` can either be in G1 or G2 """ if bls == arkworks_bls or bls == fastest_bls: - int_as_bytes = scalar.to_bytes(32, 'little') - scalar = arkworks_Scalar.from_le_bytes(int_as_bytes) + if not isinstance(scalar, arkworks_Scalar): + return point * arkworks_Scalar(int(scalar)) return point * scalar - return py_ecc_mul(point, scalar) + return py_ecc_mul(point, int(scalar)) -def multi_exp(points, integers): +def multi_exp(points, scalars): """ Performs a multi-scalar multiplication between - `points` and `integers`. + `points` and `scalars`. `points` can either be in G1 or G2. """ # Since this method accepts either G1 or G2, we need to know # the type of the point to return. Hence, we need at least one point. - if not points or not integers: - raise Exception("Cannot call multi_exp with zero points or zero integers") + if not points or not scalars: + raise Exception("Cannot call multi_exp with zero points or zero scalars") if bls == arkworks_bls or bls == fastest_bls: - # Convert integers into arkworks Scalars - scalars = [] - for integer in integers: - int_as_bytes = integer.to_bytes(32, 'little') - scalars.append(arkworks_Scalar.from_le_bytes(int_as_bytes)) + # If using py_ecc Scalars, convert to arkworks Scalars. + if not isinstance(scalars[0], arkworks_Scalar): + scalars = [arkworks_Scalar(int(s)) for s in scalars] # Check if we need to perform a G1 or G2 multiexp if isinstance(points[0], arkworks_G1): @@ -261,7 +291,7 @@ def multi_exp(points, integers): else: raise Exception("Invalid point type") - for point, scalar in zip(points, integers): + for point, scalar in zip(points, scalars): result = add(result, multiply(point, scalar)) return result From e46ba7fed227d18d9cacea29444b52fb08f60c5f Mon Sep 17 00:00:00 2001 From: terence tsao Date: Fri, 27 Sep 2024 21:12:37 -0700 Subject: [PATCH 072/103] Fix insufficient effective bal test and add a bal test --- .../test_process_withdrawal_request.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py index c216b297cb..587ba60581 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py @@ -658,6 +658,8 @@ def test_insufficient_effective_balance(spec, state): state.validators[ validator_index ].effective_balance -= spec.EFFECTIVE_BALANCE_INCREMENT + # Make sure validator has enough balance to withdraw + state.balances[validator_index] += spec.EFFECTIVE_BALANCE_INCREMENT set_compounding_withdrawal_credential(spec, state, validator_index, address=address) withdrawal_request = spec.WithdrawalRequest( @@ -787,6 +789,30 @@ def test_partial_withdrawal_activation_epoch_less_than_shard_committee_period( ) +def test_insufficient_balance(spec, state): + rng = random.Random(1361) + state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + current_epoch = spec.get_current_epoch(state) + validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch)) + validator_pubkey = state.validators[validator_index].pubkey + address = b"\x22" * 20 + amount = spec.EFFECTIVE_BALANCE_INCREMENT + + # Validator will not be able to partial withdrawal because MIN_ACTIVATION_BALANCE + amount > balance + set_compounding_withdrawal_credential(spec, state, validator_index, address=address) + withdrawal_request = spec.WithdrawalRequest( + source_address=address, + validator_pubkey=validator_pubkey, + amount=amount, + ) + + yield from run_withdrawal_request_processing( + spec, + state, + withdrawal_request, + success=False, + ) + # # Run processing # From 0301ec7ae0d67b445c80f16955d157763af88ab7 Mon Sep 17 00:00:00 2001 From: Suphanat Chunhapanya Date: Sun, 29 Sep 2024 10:54:39 +0700 Subject: [PATCH 073/103] EIP7251: Revamp process_effective_balance_updates Use get_max_effective_balance in process_effective_balance_updates since the logic is the same. --- specs/electra/beacon-chain.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index dbf84d8de8..745148be80 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -906,16 +906,13 @@ def process_effective_balance_updates(state: BeaconState) -> None: DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER # [Modified in Electra:EIP7251] - EFFECTIVE_BALANCE_LIMIT = ( - MAX_EFFECTIVE_BALANCE_ELECTRA if has_compounding_withdrawal_credential(validator) - else MIN_ACTIVATION_BALANCE - ) + max_effective_balance = get_max_effective_balance(validator) if ( balance + DOWNWARD_THRESHOLD < validator.effective_balance or validator.effective_balance + UPWARD_THRESHOLD < balance ): - validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, EFFECTIVE_BALANCE_LIMIT) + validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, max_effective_balance) ``` ### Execution engine From 29552a78849b94254108bfb896f317ee3e4890f7 Mon Sep 17 00:00:00 2001 From: Justin Traglia <95511699+jtraglia@users.noreply.github.com> Date: Mon, 30 Sep 2024 09:28:07 -0500 Subject: [PATCH 074/103] Capitalize comment --- .../electra/block_processing/test_process_withdrawal_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py index 31ace55939..b29a6b9081 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py @@ -791,7 +791,7 @@ def test_partial_withdrawal_activation_epoch_less_than_shard_committee_period( @spec_state_test def test_full_exit_request_has_partial_withdrawal(spec, state): rng = random.Random(1361) - # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit + # Move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH current_epoch = spec.get_current_epoch(state) From 0c8645ec30f96da487d0b071054d512ed7222bb2 Mon Sep 17 00:00:00 2001 From: Pop Chunhapanya Date: Tue, 1 Oct 2024 00:16:50 +0700 Subject: [PATCH 075/103] EIP-7251: Flatten get_active_balance (#3949) We have both get_active_balance and get_total_active_balance which have totally different meanings, since get_active_balance uses the balance field while get_total_active_balance uses the effective_balance field. The names suggest that get_total_active_balance is the total of get_active_balance which is not true. The name of get_active_balance doesn't quite make sense and it's used only in one place, so this commit flattens the logic of get_active_balance to the place where it's used. --- specs/electra/beacon-chain.md | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 745148be80..fe1b1eb748 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -53,7 +53,6 @@ - [New `get_balance_churn_limit`](#new-get_balance_churn_limit) - [New `get_activation_exit_churn_limit`](#new-get_activation_exit_churn_limit) - [New `get_consolidation_churn_limit`](#new-get_consolidation_churn_limit) - - [New `get_active_balance`](#new-get_active_balance) - [New `get_pending_balance_to_withdraw`](#new-get_pending_balance_to_withdraw) - [Modified `get_attesting_indices`](#modified-get_attesting_indices) - [Modified `get_next_sync_committee_indices`](#modified-get_next_sync_committee_indices) @@ -539,14 +538,6 @@ def get_consolidation_churn_limit(state: BeaconState) -> Gwei: return get_balance_churn_limit(state) - get_activation_exit_churn_limit(state) ``` -#### New `get_active_balance` - -```python -def get_active_balance(state: BeaconState, validator_index: ValidatorIndex) -> Gwei: - max_effective_balance = get_max_effective_balance(state.validators[validator_index]) - return min(state.balances[validator_index], max_effective_balance) -``` - #### New `get_pending_balance_to_withdraw` ```python @@ -884,10 +875,14 @@ def process_pending_consolidations(state: BeaconState) -> None: # Churn any target excess active balance of target and raise its max switch_to_compounding_validator(state, pending_consolidation.target_index) + + # Calculate the consolidated balance + max_effective_balance = get_max_effective_balance(source_validator) + source_effective_balance = min(state.balances[pending_consolidation.source_index], max_effective_balance) + # Move active balance to target. Excess balance is withdrawable. - active_balance = get_active_balance(state, pending_consolidation.source_index) - decrease_balance(state, pending_consolidation.source_index, active_balance) - increase_balance(state, pending_consolidation.target_index, active_balance) + decrease_balance(state, pending_consolidation.source_index, source_effective_balance) + increase_balance(state, pending_consolidation.target_index, source_effective_balance) next_pending_consolidation += 1 state.pending_consolidations = state.pending_consolidations[next_pending_consolidation:] From ecb4c2aa9c1a291e5236db1fb38f78b24734ae10 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 1 Oct 2024 01:21:14 +0400 Subject: [PATCH 076/103] eip7251: Fix partial withdrawals count (#3943) --- presets/minimal/electra.yaml | 4 +- specs/electra/beacon-chain.md | 3 +- .../test_process_withdrawals.py | 73 +------- .../test_process_withdrawals.py | 107 ++++++++++++ .../eth2spec/test/helpers/withdrawals.py | 159 +++++++++++++++++- 5 files changed, 267 insertions(+), 79 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawals.py diff --git a/presets/minimal/electra.yaml b/presets/minimal/electra.yaml index ef1ce494d8..bef11551e9 100644 --- a/presets/minimal/electra.yaml +++ b/presets/minimal/electra.yaml @@ -41,5 +41,5 @@ MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 2 # Withdrawals processing # --------------------------------------------------------------- -# 2**0 ( = 1) pending withdrawals -MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 1 +# 2**1 ( = 2) pending withdrawals +MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 2 diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index fe1b1eb748..82a052c61c 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -999,6 +999,7 @@ def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], withdrawal_index = state.next_withdrawal_index validator_index = state.next_withdrawal_validator_index withdrawals: List[Withdrawal] = [] + partial_withdrawals_count = 0 # [New in Electra:EIP7251] Consume pending partial withdrawals for withdrawal in state.pending_partial_withdrawals: @@ -1018,7 +1019,7 @@ def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], )) withdrawal_index += WithdrawalIndex(1) - partial_withdrawals_count = len(withdrawals) + partial_withdrawals_count += 1 # Sweep for remaining. bound = min(len(state.validators), MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP) diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py index 891ad35ed6..5d3407e956 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py @@ -2,7 +2,6 @@ from eth2spec.test.context import ( spec_state_test, - expect_assertion_error, with_presets, with_capella_and_later, ) @@ -24,80 +23,10 @@ set_eth1_withdrawal_credential_with_balance, set_validator_fully_withdrawable, set_validator_partially_withdrawable, + run_withdrawals_processing, ) -def verify_post_state(state, spec, expected_withdrawals, - fully_withdrawable_indices, partial_withdrawals_indices): - # Consider verifying also the condition when no withdrawals are expected. - if len(expected_withdrawals) == 0: - return - - expected_withdrawals_validator_indices = [withdrawal.validator_index for withdrawal in expected_withdrawals] - assert state.next_withdrawal_index == expected_withdrawals[-1].index + 1 - - if len(expected_withdrawals) == spec.MAX_WITHDRAWALS_PER_PAYLOAD: - # NOTE: ideally we would also check in the case with - # fewer than maximum withdrawals but that requires the pre-state info - next_withdrawal_validator_index = (expected_withdrawals_validator_indices[-1] + 1) % len(state.validators) - assert state.next_withdrawal_validator_index == next_withdrawal_validator_index - - for index in fully_withdrawable_indices: - if index in expected_withdrawals_validator_indices: - assert state.balances[index] == 0 - else: - assert state.balances[index] > 0 - for index in partial_withdrawals_indices: - if index in expected_withdrawals_validator_indices: - assert state.balances[index] == spec.MAX_EFFECTIVE_BALANCE - else: - assert state.balances[index] > spec.MAX_EFFECTIVE_BALANCE - - -def run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=None, - fully_withdrawable_indices=None, partial_withdrawals_indices=None, valid=True): - """ - Run ``process_withdrawals``, yielding: - - pre-state ('pre') - - execution payload ('execution_payload') - - post-state ('post'). - If ``valid == False``, run expecting ``AssertionError`` - """ - expected_withdrawals = get_expected_withdrawals(spec, state) - assert len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD - if num_expected_withdrawals is not None: - assert len(expected_withdrawals) == num_expected_withdrawals - - pre_state = state.copy() - yield 'pre', state - yield 'execution_payload', execution_payload - - if not valid: - expect_assertion_error(lambda: spec.process_withdrawals(state, execution_payload)) - yield 'post', None - return - - spec.process_withdrawals(state, execution_payload) - - yield 'post', state - - if len(expected_withdrawals) == 0: - next_withdrawal_validator_index = ( - pre_state.next_withdrawal_validator_index + spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP - ) - assert state.next_withdrawal_validator_index == next_withdrawal_validator_index % len(state.validators) - elif len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD: - bound = min(spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP, spec.MAX_WITHDRAWALS_PER_PAYLOAD) - assert len(get_expected_withdrawals(spec, state)) <= bound - elif len(expected_withdrawals) > spec.MAX_WITHDRAWALS_PER_PAYLOAD: - raise ValueError('len(expected_withdrawals) should not be greater than MAX_WITHDRAWALS_PER_PAYLOAD') - - 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) - - return expected_withdrawals - - @with_capella_and_later @spec_state_test def test_success_zero_expected_withdrawals(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawals.py new file mode 100644 index 0000000000..555eae85b5 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawals.py @@ -0,0 +1,107 @@ +import random + +from eth2spec.test.context import ( + spec_state_test, + with_electra_and_later, +) +from eth2spec.test.helpers.execution_payload import ( + build_empty_execution_payload, +) +from eth2spec.test.helpers.state import ( + next_slot, +) +from eth2spec.test.helpers.withdrawals import ( + prepare_expected_withdrawals_compounding, + run_withdrawals_processing, + set_compounding_withdrawal_credential_with_balance, + prepare_pending_withdrawal, +) + + +@with_electra_and_later +@spec_state_test +def test_success_mixed_fully_and_partial_withdrawable_compounding(spec, state): + num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 2 + num_partial_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD - num_full_withdrawals + fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals_compounding( + spec, state, + rng=random.Random(42), + num_full_withdrawals=num_full_withdrawals, + num_partial_withdrawals_sweep=num_partial_withdrawals, + ) + + next_slot(spec, state) + execution_payload = build_empty_execution_payload(spec, state) + + yield from run_withdrawals_processing( + spec, state, execution_payload, + fully_withdrawable_indices=fully_withdrawable_indices, + partial_withdrawals_indices=partial_withdrawals_indices) + + +@with_electra_and_later +@spec_state_test +def test_success_no_max_effective_balance_compounding(spec, state): + validator_index = len(state.validators) // 2 + # To be partially withdrawable, the validator's effective balance must be maxed out + effective_balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA - spec.EFFECTIVE_BALANCE_INCREMENT + set_compounding_withdrawal_credential_with_balance(spec, state, validator_index, effective_balance) + + validator = state.validators[validator_index] + assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index]) + + execution_payload = build_empty_execution_payload(spec, state) + + yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) + + +@with_electra_and_later +@spec_state_test +def test_success_no_excess_balance_compounding(spec, state): + validator_index = len(state.validators) // 2 + # To be partially withdrawable, the validator needs an excess balance + effective_balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + set_compounding_withdrawal_credential_with_balance(spec, state, validator_index, effective_balance) + + validator = state.validators[validator_index] + assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index]) + + execution_payload = build_empty_execution_payload(spec, state) + + yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) + + +@with_electra_and_later +@spec_state_test +def test_success_excess_balance_but_no_max_effective_balance_compounding(spec, state): + validator_index = len(state.validators) // 2 + # To be partially withdrawable, the validator needs both a maxed out effective balance and an excess balance + effective_balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA - spec.EFFECTIVE_BALANCE_INCREMENT + balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + spec.EFFECTIVE_BALANCE_INCREMENT + set_compounding_withdrawal_credential_with_balance(spec, state, validator_index, effective_balance, balance) + + validator = state.validators[validator_index] + assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index]) + + execution_payload = build_empty_execution_payload(spec, state) + + yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) + + +@with_electra_and_later +@spec_state_test +def test_pending_withdrawals_one_skipped_one_effective(spec, state): + index_0 = 3 + index_1 = 5 + + withdrawal_0 = prepare_pending_withdrawal(spec, state, index_0) + withdrawal_1 = prepare_pending_withdrawal(spec, state, index_1) + + # If validator doesn't have an excess balance pending withdrawal is skipped + state.balances[index_0] = spec.MIN_ACTIVATION_BALANCE + + execution_payload = build_empty_execution_payload(spec, state) + assert state.pending_partial_withdrawals == [withdrawal_0, withdrawal_1] + yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) + + assert state.pending_partial_withdrawals == [] diff --git a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py index 0ce476c86f..518920aeb2 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py @@ -41,14 +41,17 @@ def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance=None def set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000): - set_eth1_withdrawal_credential_with_balance(spec, state, index, spec.MAX_EFFECTIVE_BALANCE + excess_balance) validator = state.validators[index] + if is_post_electra(spec) and spec.has_compounding_withdrawal_credential(validator): + validator.effective_balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + state.balances[index] = validator.effective_balance + excess_balance + else: + set_eth1_withdrawal_credential_with_balance(spec, state, index, spec.MAX_EFFECTIVE_BALANCE + excess_balance) - assert spec.is_partially_withdrawable_validator(validator, state.balances[index]) + assert spec.is_partially_withdrawable_validator(state.validators[index], state.balances[index]) -def prepare_expected_withdrawals(spec, state, rng, - num_full_withdrawals=0, num_partial_withdrawals=0): +def sample_withdrawal_indices(spec, state, rng, num_full_withdrawals, num_partial_withdrawals): bound = min(len(state.validators), spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP) assert num_full_withdrawals + num_partial_withdrawals <= bound eligible_validator_indices = list(range(bound)) @@ -56,6 +59,15 @@ def prepare_expected_withdrawals(spec, state, rng, fully_withdrawable_indices = rng.sample(sampled_indices, num_full_withdrawals) partial_withdrawals_indices = list(set(sampled_indices).difference(set(fully_withdrawable_indices))) + return fully_withdrawable_indices, partial_withdrawals_indices + + +def prepare_expected_withdrawals(spec, state, rng, + num_full_withdrawals=0, num_partial_withdrawals=0): + fully_withdrawable_indices, partial_withdrawals_indices = sample_withdrawal_indices( + spec, state, rng, num_full_withdrawals, num_partial_withdrawals + ) + for index in fully_withdrawable_indices: set_validator_fully_withdrawable(spec, state, index) for index in partial_withdrawals_indices: @@ -70,3 +82,142 @@ def set_compounding_withdrawal_credential(spec, state, index, address=None): validator = state.validators[index] validator.withdrawal_credentials = spec.COMPOUNDING_WITHDRAWAL_PREFIX + b'\x00' * 11 + address + + +def set_compounding_withdrawal_credential_with_balance(spec, state, index, + effective_balance=None, balance=None, address=None): + set_compounding_withdrawal_credential(spec, state, index, address) + + if effective_balance is None: + effective_balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + if balance is None: + balance = effective_balance + + state.validators[index].effective_balance = effective_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, + effective_balance=32_000_000_000, amount=1_000_000_000): + assert is_post_electra(spec) + + balance = effective_balance + amount + set_compounding_withdrawal_credential_with_balance( + spec, state, validator_index, effective_balance, balance + ) + + withdrawal = spec.PendingPartialWithdrawal( + index=validator_index, + amount=amount, + withdrawable_epoch=spec.get_current_epoch(state), + ) + state.pending_partial_withdrawals.append(withdrawal) + + return withdrawal + +# +# Run processing +# + + +def verify_post_state(state, spec, expected_withdrawals, + fully_withdrawable_indices, partial_withdrawals_indices): + # Consider verifying also the condition when no withdrawals are expected. + if len(expected_withdrawals) == 0: + return + + expected_withdrawals_validator_indices = [withdrawal.validator_index for withdrawal in expected_withdrawals] + assert state.next_withdrawal_index == expected_withdrawals[-1].index + 1 + + if len(expected_withdrawals) == spec.MAX_WITHDRAWALS_PER_PAYLOAD: + # NOTE: ideally we would also check in the case with + # fewer than maximum withdrawals but that requires the pre-state info + next_withdrawal_validator_index = (expected_withdrawals_validator_indices[-1] + 1) % len(state.validators) + assert state.next_withdrawal_validator_index == next_withdrawal_validator_index + + for index in fully_withdrawable_indices: + if index in expected_withdrawals_validator_indices: + assert state.balances[index] == 0 + else: + assert state.balances[index] > 0 + for index in partial_withdrawals_indices: + if is_post_electra(spec): + max_effective_balance = spec.get_max_effective_balance(state.validators[index]) + else: + max_effective_balance = spec.MAX_EFFECTIVE_BALANCE + + if index in expected_withdrawals_validator_indices: + assert state.balances[index] == max_effective_balance + else: + assert state.balances[index] > max_effective_balance + + +def run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=None, + fully_withdrawable_indices=None, partial_withdrawals_indices=None, valid=True): + """ + Run ``process_withdrawals``, yielding: + - pre-state ('pre') + - execution payload ('execution_payload') + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + expected_withdrawals = get_expected_withdrawals(spec, state) + assert len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD + if num_expected_withdrawals is not None: + assert len(expected_withdrawals) == num_expected_withdrawals + + pre_state = state.copy() + yield 'pre', state + yield 'execution_payload', execution_payload + + if not valid: + try: + spec.process_withdrawals(state, execution_payload) + raise AssertionError('expected an assertion error, but got none.') + except AssertionError: + pass + + yield 'post', None + return + + spec.process_withdrawals(state, execution_payload) + + yield 'post', state + + if len(expected_withdrawals) == 0: + next_withdrawal_validator_index = ( + pre_state.next_withdrawal_validator_index + spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP + ) + assert state.next_withdrawal_validator_index == next_withdrawal_validator_index % len(state.validators) + elif len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD: + bound = min(spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP, spec.MAX_WITHDRAWALS_PER_PAYLOAD) + assert len(get_expected_withdrawals(spec, state)) <= bound + elif len(expected_withdrawals) > spec.MAX_WITHDRAWALS_PER_PAYLOAD: + raise ValueError('len(expected_withdrawals) should not be greater than MAX_WITHDRAWALS_PER_PAYLOAD') + + 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) + + return expected_withdrawals From 85e2452301382a9d099df16f38deac8178355358 Mon Sep 17 00:00:00 2001 From: Pop Chunhapanya Date: Tue, 1 Oct 2024 06:29:49 +0700 Subject: [PATCH 077/103] Remove queue_entire_balance_and_reset_validator (#3951) --- specs/electra/beacon-chain.md | 15 --------------- specs/electra/fork.md | 9 ++++++++- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 82a052c61c..d9c8d3557f 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -60,7 +60,6 @@ - [Modified `initiate_validator_exit`](#modified-initiate_validator_exit) - [New `switch_to_compounding_validator`](#new-switch_to_compounding_validator) - [New `queue_excess_active_balance`](#new-queue_excess_active_balance) - - [New `queue_entire_balance_and_reset_validator`](#new-queue_entire_balance_and_reset_validator) - [New `compute_exit_epoch_and_update_churn`](#new-compute_exit_epoch_and_update_churn) - [New `compute_consolidation_epoch_and_update_churn`](#new-compute_consolidation_epoch_and_update_churn) - [Modified `slash_validator`](#modified-slash_validator) @@ -646,20 +645,6 @@ def queue_excess_active_balance(state: BeaconState, index: ValidatorIndex) -> No ) ``` -#### New `queue_entire_balance_and_reset_validator` - -```python -def queue_entire_balance_and_reset_validator(state: BeaconState, index: ValidatorIndex) -> None: - balance = state.balances[index] - state.balances[index] = 0 - 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) - ) -``` - #### New `compute_exit_epoch_and_update_churn` ```python diff --git a/specs/electra/fork.md b/specs/electra/fork.md index 44c41ac0bf..b1e5da1252 100644 --- a/specs/electra/fork.md +++ b/specs/electra/fork.md @@ -152,7 +152,14 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState: )) for index in pre_activation: - queue_entire_balance_and_reset_validator(post, ValidatorIndex(index)) + balance = post.balances[index] + post.balances[index] = 0 + validator = post.validators[index] + validator.effective_balance = 0 + validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH + post.pending_balance_deposits.append( + PendingBalanceDeposit(index=index, amount=balance) + ) # Ensure early adopters of compounding credentials go through the activation churn for index, validator in enumerate(post.validators): From a7b0d6f416eec4a54dab2eb4a8a318af052453a6 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 1 Oct 2024 15:37:53 +0400 Subject: [PATCH 078/103] Apply suggestions from code review Co-authored-by: Alex Stokes Co-authored-by: Justin Traglia <95511699+jtraglia@users.noreply.github.com> --- .../test_process_consolidation_request.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py index 3259bc2d11..cdaf786fef 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py @@ -572,7 +572,7 @@ def test_switch_to_compounding_with_pending_consolidations_at_limit(spec, state) ) -# Failing tests +# Tests that should fail @with_electra_and_later @with_presets([MINIMAL], "need sufficient consolidation churn limit") @@ -958,10 +958,10 @@ def test_switch_to_compounding_exited_source(spec, state): target_pubkey=state.validators[source_index].pubkey, ) - # exit source + # Initiate exit for source spec.initiate_validator_exit(state, source_index) - # Check the the return condition + # Check the return condition assert state.validators[source_index].exit_epoch != spec.FAR_FUTURE_EPOCH yield from run_switch_to_compounding_processing( @@ -985,7 +985,7 @@ def test_switch_to_compounding_inactive_source(spec, state): target_pubkey=state.validators[source_index].pubkey, ) - # set source validator as not yet activated + # Set source validator as not yet activated state.validators[source_index].activation_epoch = spec.FAR_FUTURE_EPOCH # Check the the return condition From a04e4c3b886cc27fd95d5fa3edfa846b3c737a14 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 2 Oct 2024 13:28:09 +0400 Subject: [PATCH 079/103] Fix fork logic --- specs/electra/fork.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/specs/electra/fork.md b/specs/electra/fork.md index b293906a15..3b29e86aaf 100644 --- a/specs/electra/fork.md +++ b/specs/electra/fork.md @@ -157,9 +157,13 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState: validator = post.validators[index] validator.effective_balance = 0 validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH - post.pending_balance_deposits.append( - PendingBalanceDeposit(index=index, amount=balance) - ) + post.pending_deposits.append(PendingDeposit( + pubkey=validator.pubkey, + withdrawal_credentials=validator.withdrawal_credentials, + amount=balance, + signature=bls.G2_POINT_AT_INFINITY, + slot=GENESIS_SLOT, + )) # Ensure early adopters of compounding credentials go through the activation churn for index, validator in enumerate(post.validators): From a6294c6bd03db17a452aeb43c2d38bcbe20ba150 Mon Sep 17 00:00:00 2001 From: terence Date: Wed, 2 Oct 2024 05:45:23 -0700 Subject: [PATCH 080/103] Add a negative test for inactive validator for withdrawal request (#3945) --- .../test_process_withdrawal_request.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py index d2cee33d7e..e3ebcae7e6 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_withdrawal_request.py @@ -850,6 +850,36 @@ def test_full_exit_request_has_partial_withdrawal(spec, state): spec, state, withdrawal_request, success=False ) + +@with_electra_and_later +@spec_state_test +def test_incorrect_inactive_validator(spec, state): + rng = random.Random(1361) + # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit + state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + current_epoch = spec.get_current_epoch(state) + validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch)) + validator_pubkey = state.validators[validator_index].pubkey + address = b"\x22" * 20 + incorrect_address = b"\x33" * 20 + set_eth1_withdrawal_credential_with_balance( + spec, state, validator_index, address=address + ) + withdrawal_request = spec.WithdrawalRequest( + source_address=incorrect_address, + validator_pubkey=validator_pubkey, + amount=spec.FULL_EXIT_REQUEST_AMOUNT, + ) + + # set validator as not yet activated + state.validators[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH + assert not spec.is_active_validator(state.validators[validator_index], current_epoch) + + yield from run_withdrawal_request_processing( + spec, state, withdrawal_request, success=False + ) + # # Run processing # From 578407a4c3ed5e21e818dec1722c8327d25e8ed9 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Wed, 2 Oct 2024 12:56:47 -0500 Subject: [PATCH 081/103] Reject invalid DataColumnSidecar for zero blobs --- specs/_features/eip7594/p2p-interface.md | 7 + .../test/eip7594/unittests/test_networking.py | 147 +++++++++++++++++- 2 files changed, 153 insertions(+), 1 deletion(-) diff --git a/specs/_features/eip7594/p2p-interface.md b/specs/_features/eip7594/p2p-interface.md index 9087a8210c..301d5c3dd4 100644 --- a/specs/_features/eip7594/p2p-interface.md +++ b/specs/_features/eip7594/p2p-interface.md @@ -71,7 +71,11 @@ def verify_data_column_sidecar_kzg_proofs(sidecar: DataColumnSidecar) -> bool: """ Verify if the proofs are correct. """ + # A sidecar for zero blobs is invalid + assert len(sidecar.kzg_commitments) > 0 + # The sidecar index must be within the valid range assert sidecar.index < NUMBER_OF_COLUMNS + # There should be an equal number of cells/commitments/proofs assert len(sidecar.column) == len(sidecar.kzg_commitments) == len(sidecar.kzg_proofs) # The column index also represents the cell index @@ -93,6 +97,9 @@ def verify_data_column_sidecar_inclusion_proof(sidecar: DataColumnSidecar) -> bo """ Verify if the given KZG commitments included in the given beacon block. """ + # A sidecar for zero blobs is invalid + assert len(sidecar.kzg_commitments) > 0 + gindex = get_subtree_index(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) return is_valid_merkle_branch( leaf=hash_tree_root(sidecar.kzg_commitments), diff --git a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py index 2ab52be6c5..cad046ec53 100644 --- a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py +++ b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py @@ -1,8 +1,26 @@ +import functools +import random +import threading from eth2spec.test.context import ( - spec_test, + expect_assertion_error, single_phase, + spec_state_test, + spec_test, with_eip7594_and_later, ) +from eth2spec.debug.random_value import ( + RandomizationMode, + get_random_ssz_object, +) +from eth2spec.test.helpers.block import ( + sign_block, +) +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash, +) +from eth2spec.test.helpers.sharding import ( + get_sample_opaque_tx, +) @with_eip7594_and_later @@ -17,3 +35,130 @@ def test_compute_subnet_for_data_column_sidecar(spec): # next one should be duplicate next_subnet = spec.compute_subnet_for_data_column_sidecar(spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT) assert next_subnet == subnet_results[0] + + +@functools.cache +def compute_data_column_sidecars(spec, state): + rng = random.Random(5566) + opaque_tx, blobs, blob_kzg_commitments, _ = get_sample_opaque_tx(spec, blob_count=2) + block = get_random_ssz_object( + rng, + spec.BeaconBlock, + max_bytes_length=2000, + max_list_length=2000, + mode=RandomizationMode, + chaos=True, + ) + block.body.blob_kzg_commitments = blob_kzg_commitments + block.body.execution_payload.transactions = [opaque_tx] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + signed_block = sign_block(spec, state, block, proposer_index=0) + cells_and_kzg_proofs = [spec.compute_cells_and_kzg_proofs(blob) for blob in blobs] + return spec.get_data_column_sidecars(signed_block, cells_and_kzg_proofs) + + +# Necessary to cache the result of compute_data_column_sidecars(). +# So multiple tests do not attempt to compute the sidecars at the same time. +compute_data_column_sidecars_lock = threading.Lock() + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_kzg_proofs__valid(spec, state): + with compute_data_column_sidecars_lock: + sidecar = compute_data_column_sidecars(spec, state)[0] + assert spec.verify_data_column_sidecar_kzg_proofs(sidecar) + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_kzg_proofs__invalid_zero_blobs(spec, state): + with compute_data_column_sidecars_lock: + sidecar = compute_data_column_sidecars(spec, state)[0] + sidecar.column = [] + sidecar.kzg_commitments = [] + sidecar.kzg_proofs = [] + expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_index(spec, state): + with compute_data_column_sidecars_lock: + sidecar = compute_data_column_sidecars(spec, state)[0] + sidecar.index = 128 + expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_column(spec, state): + with compute_data_column_sidecars_lock: + sidecar = compute_data_column_sidecars(spec, state)[0] + sidecar.column = sidecar.column[1:] + expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg_commitments(spec, state): + with compute_data_column_sidecars_lock: + sidecar = compute_data_column_sidecars(spec, state)[0] + sidecar.kzg_commitments = sidecar.kzg_commitments[1:] + expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg_proofs(spec, state): + with compute_data_column_sidecars_lock: + sidecar = compute_data_column_sidecars(spec, state)[0] + sidecar.kzg_proofs = sidecar.kzg_proofs[1:] + expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_inclusion_proof__valid(spec, state): + with compute_data_column_sidecars_lock: + sidecar = compute_data_column_sidecars(spec, state)[0] + assert spec.verify_data_column_sidecar_inclusion_proof(sidecar) + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_inclusion_proof__valid_but_missing_commitment(spec, state): + with compute_data_column_sidecars_lock: + sidecar = compute_data_column_sidecars(spec, state)[0] + sidecar.kzg_commitments = sidecar.kzg_commitments[1:] + assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_inclusion_proof__valid_but_duplicate_commitment(spec, state): + with compute_data_column_sidecars_lock: + sidecar = compute_data_column_sidecars(spec, state)[0] + sidecar.kzg_commitments = sidecar.kzg_commitments + [sidecar.kzg_commitments[0]] + assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_inclusion_proof__invalid_zero_blobs(spec, state): + with compute_data_column_sidecars_lock: + sidecar = compute_data_column_sidecars(spec, state)[0] + sidecar.column = [] + sidecar.kzg_commitments = [] + sidecar.kzg_proofs = [] + expect_assertion_error(lambda: spec.verify_data_column_sidecar_inclusion_proof(sidecar)) From 3f49e6c1557467377c56d11d25a7f94514b954e8 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Wed, 2 Oct 2024 14:02:18 -0500 Subject: [PATCH 082/103] Deepcopy sidecar before modifying it --- .../test/eip7594/unittests/test_networking.py | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py index cad046ec53..585508d2da 100644 --- a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py +++ b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py @@ -1,3 +1,4 @@ +import copy import functools import random import threading @@ -38,7 +39,7 @@ def test_compute_subnet_for_data_column_sidecar(spec): @functools.cache -def compute_data_column_sidecars(spec, state): +def compute_data_column_sidecar(spec, state): rng = random.Random(5566) opaque_tx, blobs, blob_kzg_commitments, _ = get_sample_opaque_tx(spec, blob_count=2) block = get_random_ssz_object( @@ -54,20 +55,20 @@ def compute_data_column_sidecars(spec, state): block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) signed_block = sign_block(spec, state, block, proposer_index=0) cells_and_kzg_proofs = [spec.compute_cells_and_kzg_proofs(blob) for blob in blobs] - return spec.get_data_column_sidecars(signed_block, cells_and_kzg_proofs) + return spec.get_data_column_sidecars(signed_block, cells_and_kzg_proofs)[0] -# Necessary to cache the result of compute_data_column_sidecars(). +# Necessary to cache the result of compute_data_column_sidecar(). # So multiple tests do not attempt to compute the sidecars at the same time. -compute_data_column_sidecars_lock = threading.Lock() +compute_data_column_sidecar_lock = threading.Lock() @with_eip7594_and_later @spec_state_test @single_phase def test_verify_data_column_sidecar_kzg_proofs__valid(spec, state): - with compute_data_column_sidecars_lock: - sidecar = compute_data_column_sidecars(spec, state)[0] + with compute_data_column_sidecar_lock: + sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) assert spec.verify_data_column_sidecar_kzg_proofs(sidecar) @@ -75,8 +76,8 @@ def test_verify_data_column_sidecar_kzg_proofs__valid(spec, state): @spec_state_test @single_phase def test_verify_data_column_sidecar_kzg_proofs__invalid_zero_blobs(spec, state): - with compute_data_column_sidecars_lock: - sidecar = compute_data_column_sidecars(spec, state)[0] + with compute_data_column_sidecar_lock: + sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) sidecar.column = [] sidecar.kzg_commitments = [] sidecar.kzg_proofs = [] @@ -87,8 +88,8 @@ def test_verify_data_column_sidecar_kzg_proofs__invalid_zero_blobs(spec, state): @spec_state_test @single_phase def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_index(spec, state): - with compute_data_column_sidecars_lock: - sidecar = compute_data_column_sidecars(spec, state)[0] + with compute_data_column_sidecar_lock: + sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) sidecar.index = 128 expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) @@ -97,8 +98,8 @@ def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_index(spec, stat @spec_state_test @single_phase def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_column(spec, state): - with compute_data_column_sidecars_lock: - sidecar = compute_data_column_sidecars(spec, state)[0] + with compute_data_column_sidecar_lock: + sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) sidecar.column = sidecar.column[1:] expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) @@ -107,8 +108,8 @@ def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_col @spec_state_test @single_phase def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg_commitments(spec, state): - with compute_data_column_sidecars_lock: - sidecar = compute_data_column_sidecars(spec, state)[0] + with compute_data_column_sidecar_lock: + sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) sidecar.kzg_commitments = sidecar.kzg_commitments[1:] expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) @@ -117,8 +118,8 @@ def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg @spec_state_test @single_phase def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg_proofs(spec, state): - with compute_data_column_sidecars_lock: - sidecar = compute_data_column_sidecars(spec, state)[0] + with compute_data_column_sidecar_lock: + sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) sidecar.kzg_proofs = sidecar.kzg_proofs[1:] expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) @@ -127,8 +128,8 @@ def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg @spec_state_test @single_phase def test_verify_data_column_sidecar_inclusion_proof__valid(spec, state): - with compute_data_column_sidecars_lock: - sidecar = compute_data_column_sidecars(spec, state)[0] + with compute_data_column_sidecar_lock: + sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) assert spec.verify_data_column_sidecar_inclusion_proof(sidecar) @@ -136,8 +137,8 @@ def test_verify_data_column_sidecar_inclusion_proof__valid(spec, state): @spec_state_test @single_phase def test_verify_data_column_sidecar_inclusion_proof__valid_but_missing_commitment(spec, state): - with compute_data_column_sidecars_lock: - sidecar = compute_data_column_sidecars(spec, state)[0] + with compute_data_column_sidecar_lock: + sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) sidecar.kzg_commitments = sidecar.kzg_commitments[1:] assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) @@ -146,8 +147,8 @@ def test_verify_data_column_sidecar_inclusion_proof__valid_but_missing_commitmen @spec_state_test @single_phase def test_verify_data_column_sidecar_inclusion_proof__valid_but_duplicate_commitment(spec, state): - with compute_data_column_sidecars_lock: - sidecar = compute_data_column_sidecars(spec, state)[0] + with compute_data_column_sidecar_lock: + sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) sidecar.kzg_commitments = sidecar.kzg_commitments + [sidecar.kzg_commitments[0]] assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) @@ -156,8 +157,8 @@ def test_verify_data_column_sidecar_inclusion_proof__valid_but_duplicate_commitm @spec_state_test @single_phase def test_verify_data_column_sidecar_inclusion_proof__invalid_zero_blobs(spec, state): - with compute_data_column_sidecars_lock: - sidecar = compute_data_column_sidecars(spec, state)[0] + with compute_data_column_sidecar_lock: + sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) sidecar.column = [] sidecar.kzg_commitments = [] sidecar.kzg_proofs = [] From 4578b59ab7334fd10ff548e0c774d7159b6c319f Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Wed, 2 Oct 2024 14:39:09 -0500 Subject: [PATCH 083/103] Use wrapper function which does the deepcopy --- .../test/eip7594/unittests/test_networking.py | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py index 585508d2da..e91364c648 100644 --- a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py +++ b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py @@ -39,7 +39,7 @@ def test_compute_subnet_for_data_column_sidecar(spec): @functools.cache -def compute_data_column_sidecar(spec, state): +def _compute_data_column_sidecar(spec, state): rng = random.Random(5566) opaque_tx, blobs, blob_kzg_commitments, _ = get_sample_opaque_tx(spec, blob_count=2) block = get_random_ssz_object( @@ -57,6 +57,10 @@ def compute_data_column_sidecar(spec, state): cells_and_kzg_proofs = [spec.compute_cells_and_kzg_proofs(blob) for blob in blobs] return spec.get_data_column_sidecars(signed_block, cells_and_kzg_proofs)[0] +def compute_data_column_sidecar(spec, state): + """This function returns a copy of a cached data column sidecar.""" + return copy.deepcopy(_compute_data_column_sidecar(spec, state)) + # Necessary to cache the result of compute_data_column_sidecar(). # So multiple tests do not attempt to compute the sidecars at the same time. @@ -68,7 +72,7 @@ def compute_data_column_sidecar(spec, state): @single_phase def test_verify_data_column_sidecar_kzg_proofs__valid(spec, state): with compute_data_column_sidecar_lock: - sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) + sidecar = compute_data_column_sidecar(spec, state) assert spec.verify_data_column_sidecar_kzg_proofs(sidecar) @@ -77,7 +81,7 @@ def test_verify_data_column_sidecar_kzg_proofs__valid(spec, state): @single_phase def test_verify_data_column_sidecar_kzg_proofs__invalid_zero_blobs(spec, state): with compute_data_column_sidecar_lock: - sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) + sidecar = compute_data_column_sidecar(spec, state) sidecar.column = [] sidecar.kzg_commitments = [] sidecar.kzg_proofs = [] @@ -89,7 +93,7 @@ def test_verify_data_column_sidecar_kzg_proofs__invalid_zero_blobs(spec, state): @single_phase def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_index(spec, state): with compute_data_column_sidecar_lock: - sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) + sidecar = compute_data_column_sidecar(spec, state) sidecar.index = 128 expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) @@ -99,7 +103,7 @@ def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_index(spec, stat @single_phase def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_column(spec, state): with compute_data_column_sidecar_lock: - sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) + sidecar = compute_data_column_sidecar(spec, state) sidecar.column = sidecar.column[1:] expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) @@ -109,7 +113,7 @@ def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_col @single_phase def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg_commitments(spec, state): with compute_data_column_sidecar_lock: - sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) + sidecar = compute_data_column_sidecar(spec, state) sidecar.kzg_commitments = sidecar.kzg_commitments[1:] expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) @@ -119,7 +123,7 @@ def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg @single_phase def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg_proofs(spec, state): with compute_data_column_sidecar_lock: - sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) + sidecar = compute_data_column_sidecar(spec, state) sidecar.kzg_proofs = sidecar.kzg_proofs[1:] expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) @@ -129,7 +133,7 @@ def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg @single_phase def test_verify_data_column_sidecar_inclusion_proof__valid(spec, state): with compute_data_column_sidecar_lock: - sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) + sidecar = compute_data_column_sidecar(spec, state) assert spec.verify_data_column_sidecar_inclusion_proof(sidecar) @@ -138,7 +142,7 @@ def test_verify_data_column_sidecar_inclusion_proof__valid(spec, state): @single_phase def test_verify_data_column_sidecar_inclusion_proof__valid_but_missing_commitment(spec, state): with compute_data_column_sidecar_lock: - sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) + sidecar = compute_data_column_sidecar(spec, state) sidecar.kzg_commitments = sidecar.kzg_commitments[1:] assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) @@ -148,7 +152,7 @@ def test_verify_data_column_sidecar_inclusion_proof__valid_but_missing_commitmen @single_phase def test_verify_data_column_sidecar_inclusion_proof__valid_but_duplicate_commitment(spec, state): with compute_data_column_sidecar_lock: - sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) + sidecar = compute_data_column_sidecar(spec, state) sidecar.kzg_commitments = sidecar.kzg_commitments + [sidecar.kzg_commitments[0]] assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) @@ -158,7 +162,7 @@ def test_verify_data_column_sidecar_inclusion_proof__valid_but_duplicate_commitm @single_phase def test_verify_data_column_sidecar_inclusion_proof__invalid_zero_blobs(spec, state): with compute_data_column_sidecar_lock: - sidecar = copy.deepcopy(compute_data_column_sidecar(spec, state)) + sidecar = compute_data_column_sidecar(spec, state) sidecar.column = [] sidecar.kzg_commitments = [] sidecar.kzg_proofs = [] From df987b5e50960969718a2ca1d67d29c1f36ee709 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Wed, 2 Oct 2024 15:47:42 -0500 Subject: [PATCH 084/103] Create new verify_data_column_sidecar() function --- specs/_features/eip7594/p2p-interface.md | 22 ++- .../test/eip7594/unittests/test_networking.py | 161 ++++++++++-------- 2 files changed, 103 insertions(+), 80 deletions(-) diff --git a/specs/_features/eip7594/p2p-interface.md b/specs/_features/eip7594/p2p-interface.md index 301d5c3dd4..a73a955420 100644 --- a/specs/_features/eip7594/p2p-interface.md +++ b/specs/_features/eip7594/p2p-interface.md @@ -14,6 +14,7 @@ - [Containers](#containers) - [`DataColumnIdentifier`](#datacolumnidentifier) - [Helpers](#helpers) + - [`verify_data_column_sidecar`](#verify_data_column_sidecar) - [`verify_data_column_sidecar_kzg_proofs`](#verify_data_column_sidecar_kzg_proofs) - [`verify_data_column_sidecar_inclusion_proof`](#verify_data_column_sidecar_inclusion_proof) - [`compute_subnet_for_data_column_sidecar`](#compute_subnet_for_data_column_sidecar) @@ -64,12 +65,12 @@ class DataColumnIdentifier(Container): ### Helpers -##### `verify_data_column_sidecar_kzg_proofs` +##### `verify_data_column_sidecar` ```python -def verify_data_column_sidecar_kzg_proofs(sidecar: DataColumnSidecar) -> bool: +def verify_data_column_sidecar(sidecar: DataColumnSidecar) -> bool: """ - Verify if the proofs are correct. + Verify if the data column sidecar is valid. """ # A sidecar for zero blobs is invalid assert len(sidecar.kzg_commitments) > 0 @@ -78,6 +79,16 @@ def verify_data_column_sidecar_kzg_proofs(sidecar: DataColumnSidecar) -> bool: # There should be an equal number of cells/commitments/proofs assert len(sidecar.column) == len(sidecar.kzg_commitments) == len(sidecar.kzg_proofs) + return True +``` + +##### `verify_data_column_sidecar_kzg_proofs` + +```python +def verify_data_column_sidecar_kzg_proofs(sidecar: DataColumnSidecar) -> bool: + """ + Verify if the proofs are correct. + """ # The column index also represents the cell index cell_indices = [CellIndex(sidecar.index)] * len(sidecar.column) @@ -97,9 +108,6 @@ def verify_data_column_sidecar_inclusion_proof(sidecar: DataColumnSidecar) -> bo """ Verify if the given KZG commitments included in the given beacon block. """ - # A sidecar for zero blobs is invalid - assert len(sidecar.kzg_commitments) > 0 - gindex = get_subtree_index(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) return is_valid_merkle_branch( leaf=hash_tree_root(sidecar.kzg_commitments), @@ -155,7 +163,7 @@ The *type* of the payload of this topic is `DataColumnSidecar`. The following validations MUST pass before forwarding the `sidecar: DataColumnSidecar` on the network, assuming the alias `block_header = sidecar.signed_block_header.message`: -- _[REJECT]_ The sidecar's index is consistent with `NUMBER_OF_COLUMNS` -- i.e. `sidecar.index < NUMBER_OF_COLUMNS`. +- _[REJECT]_ The sidecar is valid as verified by `verify_data_column_sidecar(sidecar)`. - _[REJECT]_ The sidecar is for the correct subnet -- i.e. `compute_subnet_for_data_column_sidecar(sidecar.index) == subnet_id`. - _[IGNORE]_ The sidecar is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `block_header.slot <= current_slot` (a client MAY queue future sidecars for processing at the appropriate slot). - _[IGNORE]_ The sidecar is from a slot greater than the latest finalized slot -- i.e. validate that `block_header.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)` diff --git a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py index e91364c648..3c3b40013e 100644 --- a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py +++ b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py @@ -1,7 +1,4 @@ -import copy -import functools import random -import threading from eth2spec.test.context import ( expect_assertion_error, single_phase, @@ -24,22 +21,10 @@ ) -@with_eip7594_and_later -@spec_test -@single_phase -def test_compute_subnet_for_data_column_sidecar(spec): - subnet_results = [] - for column_index in range(spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT): - subnet_results.append(spec.compute_subnet_for_data_column_sidecar(column_index)) - # no duplicates - assert len(subnet_results) == len(set(subnet_results)) - # next one should be duplicate - next_subnet = spec.compute_subnet_for_data_column_sidecar(spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT) - assert next_subnet == subnet_results[0] +# Helper functions -@functools.cache -def _compute_data_column_sidecar(spec, state): +def compute_data_column_sidecar(spec, state): rng = random.Random(5566) opaque_tx, blobs, blob_kzg_commitments, _ = get_sample_opaque_tx(spec, blob_count=2) block = get_random_ssz_object( @@ -57,113 +42,143 @@ def _compute_data_column_sidecar(spec, state): cells_and_kzg_proofs = [spec.compute_cells_and_kzg_proofs(blob) for blob in blobs] return spec.get_data_column_sidecars(signed_block, cells_and_kzg_proofs)[0] -def compute_data_column_sidecar(spec, state): - """This function returns a copy of a cached data column sidecar.""" - return copy.deepcopy(_compute_data_column_sidecar(spec, state)) +# Tests for verify_data_column_sidecar -# Necessary to cache the result of compute_data_column_sidecar(). -# So multiple tests do not attempt to compute the sidecars at the same time. -compute_data_column_sidecar_lock = threading.Lock() +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar__valid(spec, state): + sidecar = compute_data_column_sidecar(spec, state) + assert spec.verify_data_column_sidecar(sidecar) @with_eip7594_and_later @spec_state_test @single_phase -def test_verify_data_column_sidecar_kzg_proofs__valid(spec, state): - with compute_data_column_sidecar_lock: - sidecar = compute_data_column_sidecar(spec, state) - assert spec.verify_data_column_sidecar_kzg_proofs(sidecar) +def test_verify_data_column_sidecar__invalid_zero_blobs(spec, state): + sidecar = compute_data_column_sidecar(spec, state) + sidecar.column = [] + sidecar.kzg_commitments = [] + sidecar.kzg_proofs = [] + expect_assertion_error(lambda: spec.verify_data_column_sidecar(sidecar)) @with_eip7594_and_later @spec_state_test @single_phase -def test_verify_data_column_sidecar_kzg_proofs__invalid_zero_blobs(spec, state): - with compute_data_column_sidecar_lock: - sidecar = compute_data_column_sidecar(spec, state) - sidecar.column = [] - sidecar.kzg_commitments = [] - sidecar.kzg_proofs = [] - expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) +def test_verify_data_column_sidecar__invalid_index(spec, state): + sidecar = compute_data_column_sidecar(spec, state) + sidecar.index = 128 + expect_assertion_error(lambda: spec.verify_data_column_sidecar(sidecar)) @with_eip7594_and_later @spec_state_test @single_phase -def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_index(spec, state): - with compute_data_column_sidecar_lock: - sidecar = compute_data_column_sidecar(spec, state) - sidecar.index = 128 - expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) +def test_verify_data_column_sidecar__invalid_mismatch_len_column(spec, state): + sidecar = compute_data_column_sidecar(spec, state) + sidecar.column = sidecar.column[1:] + expect_assertion_error(lambda: spec.verify_data_column_sidecar(sidecar)) @with_eip7594_and_later @spec_state_test @single_phase -def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_column(spec, state): - with compute_data_column_sidecar_lock: - sidecar = compute_data_column_sidecar(spec, state) - sidecar.column = sidecar.column[1:] - expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) +def test_verify_data_column_sidecar__invalid_mismatch_len_kzg_commitments(spec, state): + sidecar = compute_data_column_sidecar(spec, state) + sidecar.kzg_commitments = sidecar.kzg_commitments[1:] + expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) @with_eip7594_and_later @spec_state_test @single_phase -def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg_commitments(spec, state): - with compute_data_column_sidecar_lock: - sidecar = compute_data_column_sidecar(spec, state) - sidecar.kzg_commitments = sidecar.kzg_commitments[1:] - expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) +def test_verify_data_column_sidecars__invalid_mismatch_len_kzg_proofs(spec, state): + sidecar = compute_data_column_sidecar(spec, state) + sidecar.kzg_proofs = sidecar.kzg_proofs[1:] + expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) + + +# Tests for verify_data_column_sidecar_kzg_proofs + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_kzg_proofs__valid(spec, state): + sidecar = compute_data_column_sidecar(spec, state) + assert spec.verify_data_column_sidecar_kzg_proofs(sidecar) @with_eip7594_and_later @spec_state_test @single_phase -def test_verify_data_column_sidecar_kzg_proofs_invalid__invalid_mismatch_len_kzg_proofs(spec, state): - with compute_data_column_sidecar_lock: - sidecar = compute_data_column_sidecar(spec, state) - sidecar.kzg_proofs = sidecar.kzg_proofs[1:] - expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) +def test_verify_data_column_sidecar_kzg_proofs__valid_but_wrong_column(spec, state): + sidecar = compute_data_column_sidecar(spec, state) + sidecar.column[0] = sidecar.column[1] + assert not spec.verify_data_column_sidecar_kzg_proofs(sidecar) + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_kzg_proofs__valid_but_wrong_commitment(spec, state): + sidecar = compute_data_column_sidecar(spec, state) + sidecar.kzg_commitments[0] = sidecar.kzg_commitments[1] + assert not spec.verify_data_column_sidecar_kzg_proofs(sidecar) + + +@with_eip7594_and_later +@spec_state_test +@single_phase +def test_verify_data_column_sidecar_kzg_proofs__valid_but_wrong_proof(spec, state): + sidecar = compute_data_column_sidecar(spec, state) + sidecar.kzg_proofs[0] = sidecar.kzg_proofs[1] + assert not spec.verify_data_column_sidecar_kzg_proofs(sidecar) + + +# Tests for verify_data_column_sidecar_inclusion_proof @with_eip7594_and_later @spec_state_test @single_phase def test_verify_data_column_sidecar_inclusion_proof__valid(spec, state): - with compute_data_column_sidecar_lock: - sidecar = compute_data_column_sidecar(spec, state) - assert spec.verify_data_column_sidecar_inclusion_proof(sidecar) + sidecar = compute_data_column_sidecar(spec, state) + assert spec.verify_data_column_sidecar_inclusion_proof(sidecar) @with_eip7594_and_later @spec_state_test @single_phase def test_verify_data_column_sidecar_inclusion_proof__valid_but_missing_commitment(spec, state): - with compute_data_column_sidecar_lock: - sidecar = compute_data_column_sidecar(spec, state) - sidecar.kzg_commitments = sidecar.kzg_commitments[1:] - assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) + sidecar = compute_data_column_sidecar(spec, state) + sidecar.kzg_commitments = sidecar.kzg_commitments[1:] + assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) @with_eip7594_and_later @spec_state_test @single_phase def test_verify_data_column_sidecar_inclusion_proof__valid_but_duplicate_commitment(spec, state): - with compute_data_column_sidecar_lock: - sidecar = compute_data_column_sidecar(spec, state) - sidecar.kzg_commitments = sidecar.kzg_commitments + [sidecar.kzg_commitments[0]] - assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) + sidecar = compute_data_column_sidecar(spec, state) + sidecar.kzg_commitments = sidecar.kzg_commitments + [sidecar.kzg_commitments[0]] + assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) + + +# Tests for compute_subnet_for_data_column_sidecar @with_eip7594_and_later -@spec_state_test +@spec_test @single_phase -def test_verify_data_column_sidecar_inclusion_proof__invalid_zero_blobs(spec, state): - with compute_data_column_sidecar_lock: - sidecar = compute_data_column_sidecar(spec, state) - sidecar.column = [] - sidecar.kzg_commitments = [] - sidecar.kzg_proofs = [] - expect_assertion_error(lambda: spec.verify_data_column_sidecar_inclusion_proof(sidecar)) +def test_compute_subnet_for_data_column_sidecar(spec): + subnet_results = [] + for column_index in range(spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT): + subnet_results.append(spec.compute_subnet_for_data_column_sidecar(column_index)) + # no duplicates + assert len(subnet_results) == len(set(subnet_results)) + # next one should be duplicate + next_subnet = spec.compute_subnet_for_data_column_sidecar(spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT) + assert next_subnet == subnet_results[0] From 09cc20459be20df3658b56147bced663022c78e0 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Wed, 2 Oct 2024 15:53:42 -0500 Subject: [PATCH 085/103] Add an extra blank line for consistency --- .../pyspec/eth2spec/test/eip7594/unittests/test_networking.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py index 3c3b40013e..3797a4ecdb 100644 --- a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py +++ b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py @@ -45,6 +45,7 @@ def compute_data_column_sidecar(spec, state): # Tests for verify_data_column_sidecar + @with_eip7594_and_later @spec_state_test @single_phase From e02cbab59f007a7541671c497bd8a226f3fb4a4f Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Wed, 2 Oct 2024 15:55:16 -0500 Subject: [PATCH 086/103] Move index check to top --- specs/_features/eip7594/p2p-interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/_features/eip7594/p2p-interface.md b/specs/_features/eip7594/p2p-interface.md index a73a955420..712845c56f 100644 --- a/specs/_features/eip7594/p2p-interface.md +++ b/specs/_features/eip7594/p2p-interface.md @@ -72,10 +72,10 @@ def verify_data_column_sidecar(sidecar: DataColumnSidecar) -> bool: """ Verify if the data column sidecar is valid. """ - # A sidecar for zero blobs is invalid - assert len(sidecar.kzg_commitments) > 0 # The sidecar index must be within the valid range assert sidecar.index < NUMBER_OF_COLUMNS + # A sidecar for zero blobs is invalid + assert len(sidecar.kzg_commitments) > 0 # There should be an equal number of cells/commitments/proofs assert len(sidecar.column) == len(sidecar.kzg_commitments) == len(sidecar.kzg_proofs) From 3984bd36041bb974ad22cc624b82d88b6c30e601 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Wed, 2 Oct 2024 16:02:39 -0500 Subject: [PATCH 087/103] Convert assert to return False --- specs/_features/eip7594/p2p-interface.md | 11 ++++++++--- .../test/eip7594/unittests/test_networking.py | 11 +++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/specs/_features/eip7594/p2p-interface.md b/specs/_features/eip7594/p2p-interface.md index 712845c56f..3dd1994255 100644 --- a/specs/_features/eip7594/p2p-interface.md +++ b/specs/_features/eip7594/p2p-interface.md @@ -73,11 +73,16 @@ def verify_data_column_sidecar(sidecar: DataColumnSidecar) -> bool: Verify if the data column sidecar is valid. """ # The sidecar index must be within the valid range - assert sidecar.index < NUMBER_OF_COLUMNS + if sidecar.index >= NUMBER_OF_COLUMNS + return False + # A sidecar for zero blobs is invalid - assert len(sidecar.kzg_commitments) > 0 + if len(sidecar.kzg_commitments) == 0: + return False + # There should be an equal number of cells/commitments/proofs - assert len(sidecar.column) == len(sidecar.kzg_commitments) == len(sidecar.kzg_proofs) + if len(sidecar.column) != len(sidecar.kzg_commitments) or len(sidecar.column) != len(sidecar.kzg_proofs): + return False return True ``` diff --git a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py index 3797a4ecdb..a818c746fd 100644 --- a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py +++ b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py @@ -1,6 +1,5 @@ import random from eth2spec.test.context import ( - expect_assertion_error, single_phase, spec_state_test, spec_test, @@ -62,7 +61,7 @@ def test_verify_data_column_sidecar__invalid_zero_blobs(spec, state): sidecar.column = [] sidecar.kzg_commitments = [] sidecar.kzg_proofs = [] - expect_assertion_error(lambda: spec.verify_data_column_sidecar(sidecar)) + assert not spec.verify_data_column_sidecar(sidecar) @with_eip7594_and_later @@ -71,7 +70,7 @@ def test_verify_data_column_sidecar__invalid_zero_blobs(spec, state): def test_verify_data_column_sidecar__invalid_index(spec, state): sidecar = compute_data_column_sidecar(spec, state) sidecar.index = 128 - expect_assertion_error(lambda: spec.verify_data_column_sidecar(sidecar)) + assert not spec.verify_data_column_sidecar(sidecar) @with_eip7594_and_later @@ -80,7 +79,7 @@ def test_verify_data_column_sidecar__invalid_index(spec, state): def test_verify_data_column_sidecar__invalid_mismatch_len_column(spec, state): sidecar = compute_data_column_sidecar(spec, state) sidecar.column = sidecar.column[1:] - expect_assertion_error(lambda: spec.verify_data_column_sidecar(sidecar)) + assert not spec.verify_data_column_sidecar(sidecar) @with_eip7594_and_later @@ -89,7 +88,7 @@ def test_verify_data_column_sidecar__invalid_mismatch_len_column(spec, state): def test_verify_data_column_sidecar__invalid_mismatch_len_kzg_commitments(spec, state): sidecar = compute_data_column_sidecar(spec, state) sidecar.kzg_commitments = sidecar.kzg_commitments[1:] - expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) + assert not spec.verify_data_column_sidecar(sidecar) @with_eip7594_and_later @@ -98,7 +97,7 @@ def test_verify_data_column_sidecar__invalid_mismatch_len_kzg_commitments(spec, def test_verify_data_column_sidecars__invalid_mismatch_len_kzg_proofs(spec, state): sidecar = compute_data_column_sidecar(spec, state) sidecar.kzg_proofs = sidecar.kzg_proofs[1:] - expect_assertion_error(lambda: spec.verify_data_column_sidecar_kzg_proofs(sidecar)) + assert not spec.verify_data_column_sidecar(sidecar) # Tests for verify_data_column_sidecar_kzg_proofs From 3196487a689056d7d26ef1ed6d7daf4414d70658 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Wed, 2 Oct 2024 16:05:10 -0500 Subject: [PATCH 088/103] Add missing semicolon --- specs/_features/eip7594/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/eip7594/p2p-interface.md b/specs/_features/eip7594/p2p-interface.md index 3dd1994255..710b9f482c 100644 --- a/specs/_features/eip7594/p2p-interface.md +++ b/specs/_features/eip7594/p2p-interface.md @@ -73,7 +73,7 @@ def verify_data_column_sidecar(sidecar: DataColumnSidecar) -> bool: Verify if the data column sidecar is valid. """ # The sidecar index must be within the valid range - if sidecar.index >= NUMBER_OF_COLUMNS + if sidecar.index >= NUMBER_OF_COLUMNS: return False # A sidecar for zero blobs is invalid From 2ceb0fd74da30f081caedd6c136646ef2d367030 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Wed, 2 Oct 2024 16:51:46 -0500 Subject: [PATCH 089/103] Fix some nits --- specs/_features/eip7594/p2p-interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/_features/eip7594/p2p-interface.md b/specs/_features/eip7594/p2p-interface.md index 710b9f482c..4c4ae83efb 100644 --- a/specs/_features/eip7594/p2p-interface.md +++ b/specs/_features/eip7594/p2p-interface.md @@ -80,7 +80,7 @@ def verify_data_column_sidecar(sidecar: DataColumnSidecar) -> bool: if len(sidecar.kzg_commitments) == 0: return False - # There should be an equal number of cells/commitments/proofs + # The column length must be equal to the number of commitments/proofs if len(sidecar.column) != len(sidecar.kzg_commitments) or len(sidecar.column) != len(sidecar.kzg_proofs): return False @@ -92,7 +92,7 @@ def verify_data_column_sidecar(sidecar: DataColumnSidecar) -> bool: ```python def verify_data_column_sidecar_kzg_proofs(sidecar: DataColumnSidecar) -> bool: """ - Verify if the proofs are correct. + Verify if the KZG proofs are correct. """ # The column index also represents the cell index cell_indices = [CellIndex(sidecar.index)] * len(sidecar.column) From 3e80dc8ed02000ab9f3faee036d1fe565461d5eb Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Thu, 3 Oct 2024 07:42:45 -0500 Subject: [PATCH 090/103] Add new verification to is_data_available --- specs/_features/eip7594/fork-choice.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/_features/eip7594/fork-choice.md b/specs/_features/eip7594/fork-choice.md index a6ddf05087..d40d134192 100644 --- a/specs/_features/eip7594/fork-choice.md +++ b/specs/_features/eip7594/fork-choice.md @@ -31,6 +31,7 @@ def is_data_available(beacon_block_root: Root) -> bool: # `MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS` epochs. column_sidecars = retrieve_column_sidecars(beacon_block_root) return all( + verify_data_column_sidecar(column_sidecar) and verify_data_column_sidecar_kzg_proofs(column_sidecar) for column_sidecar in column_sidecars ) From 1329410f0ec1506ac3fef69696b164563d585218 Mon Sep 17 00:00:00 2001 From: Justin Traglia <95511699+jtraglia@users.noreply.github.com> Date: Thu, 3 Oct 2024 07:44:25 -0500 Subject: [PATCH 091/103] Bump version to 1.5.0-alpha.7 (#3955) --- tests/core/pyspec/eth2spec/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/VERSION.txt b/tests/core/pyspec/eth2spec/VERSION.txt index f49b638a29..d1cdd9f1e4 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -1.5.0-alpha.6 +1.5.0-alpha.7 From 2dd9e82306dd4a1a9a91877b7267087b1b21d13f Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 3 Oct 2024 17:51:48 +0400 Subject: [PATCH 092/103] Remove blank lines --- .../block_processing/test_process_consolidation_request.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py index cdaf786fef..b1ee7e1213 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py @@ -1100,7 +1100,6 @@ def run_consolidation_processing(spec, state, consolidation, success=True): - post-state ('post'). If ``success == False``, ``process_consolidation_request`` would return without any state change. """ - if success: validator_pubkeys = [v.pubkey for v in state.validators] source_index = spec.ValidatorIndex(validator_pubkeys.index(consolidation.source_pubkey)) @@ -1170,7 +1169,6 @@ def run_switch_to_compounding_processing(spec, state, consolidation, success=Tru - post-state ('post'). If ``success == False``, ``process_consolidation_request`` would return without any state change. """ - if success: validator_pubkeys = [v.pubkey for v in state.validators] source_index = spec.ValidatorIndex(validator_pubkeys.index(consolidation.source_pubkey)) From 66f5c3776cf9aa62e0b324c658bd3dc276a8775c Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 3 Oct 2024 17:55:39 +0400 Subject: [PATCH 093/103] Fix switch to compounding tests --- .../block_processing/test_process_consolidation_request.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py index b1ee7e1213..426f4fbe79 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py @@ -1003,9 +1003,8 @@ def test_switch_to_compounding_source_bls_withdrawal_credential(spec, state): # a bls withdrawal credential current_epoch = spec.get_current_epoch(state) source_index = spec.get_active_validator_indices(state, current_epoch)[0] - source_address = b"\x22" * 20 consolidation = spec.ConsolidationRequest( - source_address=source_address, + source_address=state.validators[source_index].withdrawal_credentials[12:], source_pubkey=state.validators[source_index].pubkey, target_pubkey=state.validators[source_index].pubkey, ) @@ -1026,12 +1025,12 @@ def test_switch_to_compounding_source_coumpounding_withdrawal_credential(spec, s current_epoch = spec.get_current_epoch(state) source_index = spec.get_active_validator_indices(state, current_epoch)[0] source_address = b"\x22" * 20 + set_compounding_withdrawal_credential(spec, state, source_index, address=source_address) consolidation = spec.ConsolidationRequest( source_address=source_address, source_pubkey=state.validators[source_index].pubkey, target_pubkey=state.validators[source_index].pubkey, ) - set_compounding_withdrawal_credential(spec, state, source_index) state.balances[source_index] = spec.MIN_ACTIVATION_BALANCE + spec.EFFECTIVE_BALANCE_INCREMENT # Check the the return condition From beb27181824e68493aeff22feb88033fe7afcc7a Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 3 Oct 2024 18:49:08 +0400 Subject: [PATCH 094/103] Apply suggestions by @ppopth --- specs/electra/beacon-chain.md | 24 ++++++++++++------------ specs/electra/fork.md | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 51b4bd836f..4adc810695 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -653,6 +653,8 @@ def queue_excess_active_balance(state: BeaconState, index: ValidatorIndex) -> No excess_balance = balance - MIN_ACTIVATION_BALANCE state.balances[index] = MIN_ACTIVATION_BALANCE validator = state.validators[index] + # Use bls.G2_POINT_AT_INFINITY as a signature field placeholder + # and GENESIS_SLOT to distinguish from a pending deposit request state.pending_deposits.append(PendingDeposit( pubkey=validator.pubkey, withdrawal_credentials=validator.withdrawal_credentials, @@ -1274,23 +1276,21 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: ```python def get_validator_from_deposit(pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64) -> Validator: - if is_compounding_withdrawal_credential(withdrawal_credentials): - max_effective_balance = MAX_EFFECTIVE_BALANCE_ELECTRA - else: - max_effective_balance = MIN_ACTIVATION_BALANCE - - # [Modified in Electra:EIP7251] - effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, max_effective_balance) - - return Validator( + validator = 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=effective_balance, + effective_balance=Gwei(0), ) + + # [Modified in Electra:EIP7251] + max_effective_balance = get_max_effective_balance(validator) + validator.effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, max_effective_balance) + + return validator ``` ###### Modified `add_validator_to_registry` @@ -1332,7 +1332,7 @@ def apply_deposit(state: BeaconState, withdrawal_credentials=withdrawal_credentials, amount=amount, signature=signature, - slot=GENESIS_SLOT, + slot=GENESIS_SLOT, # Use GENESIS_SLOT to distinguish from a pending deposit request )) else: # Increase balance by deposit amount @@ -1342,7 +1342,7 @@ def apply_deposit(state: BeaconState, withdrawal_credentials=withdrawal_credentials, amount=amount, signature=signature, - slot=GENESIS_SLOT + slot=GENESIS_SLOT # Use GENESIS_SLOT to distinguish from a pending deposit request )) ``` diff --git a/specs/electra/fork.md b/specs/electra/fork.md index 3b29e86aaf..6ac5be5b03 100644 --- a/specs/electra/fork.md +++ b/specs/electra/fork.md @@ -157,6 +157,8 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState: validator = post.validators[index] validator.effective_balance = 0 validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH + # Use bls.G2_POINT_AT_INFINITY as a signature field placeholder + # and GENESIS_SLOT to distinguish from a pending deposit request post.pending_deposits.append(PendingDeposit( pubkey=validator.pubkey, withdrawal_credentials=validator.withdrawal_credentials, From 30f6aba593572c84c3bc8d21d4256402e5993e06 Mon Sep 17 00:00:00 2001 From: Justin Traglia <95511699+jtraglia@users.noreply.github.com> Date: Thu, 3 Oct 2024 11:04:33 -0500 Subject: [PATCH 095/103] Put "and" at the beginning of the next line Co-authored-by: Hsiao-Wei Wang --- specs/_features/eip7594/fork-choice.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/_features/eip7594/fork-choice.md b/specs/_features/eip7594/fork-choice.md index d40d134192..f406b7472a 100644 --- a/specs/_features/eip7594/fork-choice.md +++ b/specs/_features/eip7594/fork-choice.md @@ -31,8 +31,8 @@ def is_data_available(beacon_block_root: Root) -> bool: # `MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS` epochs. column_sidecars = retrieve_column_sidecars(beacon_block_root) return all( - verify_data_column_sidecar(column_sidecar) and - verify_data_column_sidecar_kzg_proofs(column_sidecar) + verify_data_column_sidecar(column_sidecar) + and verify_data_column_sidecar_kzg_proofs(column_sidecar) for column_sidecar in column_sidecars ) ``` From 62c32da22bec597b05e5796e10515a78345f6589 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Thu, 3 Oct 2024 11:20:38 -0500 Subject: [PATCH 096/103] Be more consistent with test names --- .../eth2spec/test/eip7594/unittests/test_networking.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py index a818c746fd..633508f9a2 100644 --- a/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py +++ b/tests/core/pyspec/eth2spec/test/eip7594/unittests/test_networking.py @@ -114,7 +114,7 @@ def test_verify_data_column_sidecar_kzg_proofs__valid(spec, state): @with_eip7594_and_later @spec_state_test @single_phase -def test_verify_data_column_sidecar_kzg_proofs__valid_but_wrong_column(spec, state): +def test_verify_data_column_sidecar_kzg_proofs__invalid_wrong_column(spec, state): sidecar = compute_data_column_sidecar(spec, state) sidecar.column[0] = sidecar.column[1] assert not spec.verify_data_column_sidecar_kzg_proofs(sidecar) @@ -123,7 +123,7 @@ def test_verify_data_column_sidecar_kzg_proofs__valid_but_wrong_column(spec, sta @with_eip7594_and_later @spec_state_test @single_phase -def test_verify_data_column_sidecar_kzg_proofs__valid_but_wrong_commitment(spec, state): +def test_verify_data_column_sidecar_kzg_proofs__invalid_wrong_commitment(spec, state): sidecar = compute_data_column_sidecar(spec, state) sidecar.kzg_commitments[0] = sidecar.kzg_commitments[1] assert not spec.verify_data_column_sidecar_kzg_proofs(sidecar) @@ -132,7 +132,7 @@ def test_verify_data_column_sidecar_kzg_proofs__valid_but_wrong_commitment(spec, @with_eip7594_and_later @spec_state_test @single_phase -def test_verify_data_column_sidecar_kzg_proofs__valid_but_wrong_proof(spec, state): +def test_verify_data_column_sidecar_kzg_proofs__invalid_wrong_proof(spec, state): sidecar = compute_data_column_sidecar(spec, state) sidecar.kzg_proofs[0] = sidecar.kzg_proofs[1] assert not spec.verify_data_column_sidecar_kzg_proofs(sidecar) @@ -152,7 +152,7 @@ def test_verify_data_column_sidecar_inclusion_proof__valid(spec, state): @with_eip7594_and_later @spec_state_test @single_phase -def test_verify_data_column_sidecar_inclusion_proof__valid_but_missing_commitment(spec, state): +def test_verify_data_column_sidecar_inclusion_proof__invalid_missing_commitment(spec, state): sidecar = compute_data_column_sidecar(spec, state) sidecar.kzg_commitments = sidecar.kzg_commitments[1:] assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) @@ -161,7 +161,7 @@ def test_verify_data_column_sidecar_inclusion_proof__valid_but_missing_commitmen @with_eip7594_and_later @spec_state_test @single_phase -def test_verify_data_column_sidecar_inclusion_proof__valid_but_duplicate_commitment(spec, state): +def test_verify_data_column_sidecar_inclusion_proof__invalid_duplicate_commitment(spec, state): sidecar = compute_data_column_sidecar(spec, state) sidecar.kzg_commitments = sidecar.kzg_commitments + [sidecar.kzg_commitments[0]] assert not spec.verify_data_column_sidecar_inclusion_proof(sidecar) From f97cd619f4a10c851f89f3f7253081da432dcd24 Mon Sep 17 00:00:00 2001 From: Suphanat Chunhapanya Date: Fri, 4 Oct 2024 00:51:04 +0700 Subject: [PATCH 097/103] Correct the use of get_data_column_sidecars --- specs/_features/eip7594/das-core.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/eip7594/das-core.md b/specs/_features/eip7594/das-core.md index 25e58a1334..6a1d2758e0 100644 --- a/specs/_features/eip7594/das-core.md +++ b/specs/_features/eip7594/das-core.md @@ -244,7 +244,7 @@ In this construction, we extend the blobs using a one-dimensional erasure coding ### Parameters -For each column -- use `data_column_sidecar_{subnet_id}` subnets, where `subnet_id` can be computed with the `compute_subnet_for_data_column_sidecar(column_index: ColumnIndex)` helper. The sidecars can be computed with the `get_data_column_sidecars(signed_block: SignedBeaconBlock, blobs: Sequence[Blob])` helper. +For each column -- use `data_column_sidecar_{subnet_id}` subnets, where `subnet_id` can be computed with the `compute_subnet_for_data_column_sidecar(column_index: ColumnIndex)` helper. The sidecars can be computed with `cells_and_kzg_proofs = [compute_cells_and_kzg_proofs(blob) for blob in blobs]` and then `get_data_column_sidecars(signed_block, cells_and_kzg_proofs)`. Verifiable samples from their respective column are distributed on the assigned subnet. To custody a particular column, a node joins the respective gossipsub subnet. If a node fails to get a column on the column subnet, a node can also utilize the Req/Resp protocol to query the missing column from other peers. From 82d2bc0d96898506959f0960015d3157e777834a Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Thu, 3 Oct 2024 14:43:11 -0500 Subject: [PATCH 098/103] Fix two tests by replacing old PendingBalanceDeposit --- .../test_process_consolidation_request.py | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py index 426f4fbe79..49744946f0 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation_request.py @@ -1148,12 +1148,19 @@ def run_consolidation_processing(spec, state, consolidation, success=True): assert state.pending_consolidations == pre_pending_consolidations + [expected_new_pending_consolidation] # Check excess balance is queued if the target switched to compounding if pre_target_withdrawal_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX: - assert state.validators[target_index].withdrawal_credentials == ( - spec.COMPOUNDING_WITHDRAWAL_PREFIX + pre_target_withdrawal_credentials[1:]) + post_target_withdrawal_credentials = ( + spec.COMPOUNDING_WITHDRAWAL_PREFIX + pre_target_withdrawal_credentials[1:] + ) + assert state.validators[target_index].withdrawal_credentials == post_target_withdrawal_credentials assert state.balances[target_index] == spec.MIN_ACTIVATION_BALANCE if pre_target_balance > spec.MIN_ACTIVATION_BALANCE: - assert state.pending_balance_deposits == [spec.PendingBalanceDeposit( - index=target_index, amount=(pre_target_balance - spec.MIN_ACTIVATION_BALANCE))] + assert len(state.pending_deposits) == 1 + pending_deposit = state.pending_deposits[0] + assert pending_deposit.pubkey == target_validator.pubkey + assert pending_deposit.withdrawal_credentials == post_target_withdrawal_credentials + assert pending_deposit.amount == (pre_target_balance - spec.MIN_ACTIVATION_BALANCE) + assert pending_deposit.signature == spec.G2_POINT_AT_INFINITY + assert pending_deposit.slot == spec.GENESIS_SLOT else: assert state.balances[target_index] == pre_target_balance else: @@ -1194,14 +1201,20 @@ def run_switch_to_compounding_processing(spec, state, consolidation, success=Tru # Check source address in the consolidation fits the withdrawal credentials assert state.validators[source_index].withdrawal_credentials[12:] == consolidation.source_address # Check that the source has switched to compounding - assert state.validators[source_index].withdrawal_credentials == ( + post_withdrawal_credentials = ( spec.COMPOUNDING_WITHDRAWAL_PREFIX + pre_withdrawal_credentials[1:] ) + assert state.validators[source_index].withdrawal_credentials == post_withdrawal_credentials # Check excess balance is queued assert state.balances[source_index] == spec.MIN_ACTIVATION_BALANCE if pre_balance > spec.MIN_ACTIVATION_BALANCE: - assert state.pending_balance_deposits == [spec.PendingBalanceDeposit( - index=source_index, amount=(pre_balance - spec.MIN_ACTIVATION_BALANCE))] + assert len(state.pending_deposits) == 1 + pending_deposit = state.pending_deposits[0] + assert pending_deposit.pubkey == source_validator.pubkey + assert pending_deposit.withdrawal_credentials == post_withdrawal_credentials + assert pending_deposit.amount == (pre_balance - spec.MIN_ACTIVATION_BALANCE) + assert pending_deposit.signature == spec.G2_POINT_AT_INFINITY + assert pending_deposit.slot == spec.GENESIS_SLOT # Check no consolidation has been initiated assert state.validators[source_index].exit_epoch == spec.FAR_FUTURE_EPOCH assert state.pending_consolidations == pre_pending_consolidations From ef65fbc394404fea4b2d2fa52de31f1708ef270e Mon Sep 17 00:00:00 2001 From: Lucas Saldanha Date: Fri, 4 Oct 2024 09:43:18 +1300 Subject: [PATCH 099/103] Fix EIP reference --- specs/electra/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 111dc5b970..9b6d705441 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -170,7 +170,7 @@ The following values are (non-configurable) constants used throughout the specif | - | - | - | | `MAX_DEPOSIT_REQUESTS_PER_PAYLOAD` | `uint64(2**13)` (= 8,192) | *[New in Electra:EIP6110]* Maximum number of deposit receipts allowed in each payload | | `MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD` | `uint64(2**4)` (= 16)| *[New in Electra:EIP7002]* Maximum number of execution layer withdrawal requests in each payload | -| `MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD` | `uint64(1)` (= 1) | *[New in Electra:EIP7002]* Maximum number of execution layer consolidation requests in each payload | +| `MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD` | `uint64(1)` (= 1) | *[New in Electra:EIP7251]* Maximum number of execution layer consolidation requests in each payload | ### Withdrawals processing From 770598438e5842784fbf63f634ee113d410b8b71 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 4 Oct 2024 10:25:58 +0400 Subject: [PATCH 100/103] Fix consolidation test --- .../test_process_pending_consolidations.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py index b897bd514b..322224b78e 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_consolidations.py @@ -199,19 +199,6 @@ def test_pending_consolidation_future_epoch(spec, state): assert state.balances[target_index] == expected_target_balance assert state.pending_consolidations == [] - # Pending balance deposit to the target is created as part of `switch_to_compounding_validator`. - # The excess balance to queue are the rewards accumulated over the previous epoch transitions. - expected_pending_balance = state_before_consolidation.balances[target_index] - spec.MIN_ACTIVATION_BALANCE - target = state.validators[target_index] - pending_deposit = spec.PendingDeposit( - pubkey=target.pubkey, - withdrawal_credentials=target.withdrawal_credentials, - amount=expected_pending_balance, - signature=spec.bls.G2_POINT_AT_INFINITY, - slot=spec.GENESIS_SLOT, - ) - assert state.pending_deposits == [pending_deposit] - @with_electra_and_later @spec_state_test From 77d2865eeeab964e9a7395cd731db7d95c3f851d Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Fri, 4 Oct 2024 09:22:16 -0500 Subject: [PATCH 101/103] Rename test_apply_pending_deposit to test_process_pending_deposit --- ...apply_pending_deposit.py => test_process_pending_deposit.py} | 2 +- tests/generators/epoch_processing/main.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) rename tests/core/pyspec/eth2spec/test/electra/epoch_processing/{test_apply_pending_deposit.py => test_process_pending_deposit.py} (99%) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposit.py similarity index 99% rename from tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py rename to tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposit.py index cc3c2205a7..0c920d56a3 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_apply_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposit.py @@ -3,7 +3,7 @@ with_electra_and_later, always_bls, ) -from tests.core.pyspec.eth2spec.test.helpers.deposits import ( +from eth2spec.test.helpers.deposits import ( prepare_pending_deposit, run_pending_deposit_applying, ) diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index 6e7c3e376e..c420da2e93 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -39,6 +39,7 @@ _new_electra_mods = {key: 'eth2spec.test.electra.epoch_processing.test_process_' + key for key in [ 'effective_balance_updates', + 'pending_deposit', 'pending_deposits', 'pending_consolidations', ]} From 3befc4279b02df1c2c70cc0f3fb9dc046a754951 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Fri, 4 Oct 2024 10:57:01 -0500 Subject: [PATCH 102/103] Rename test_apply_pending_deposit* to test_process_pending_deposit* --- .../test_process_pending_deposit.py | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposit.py index 0c920d56a3..ad9a70f3a7 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposit.py @@ -13,7 +13,7 @@ @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_under_min_activation(spec, state): +def test_process_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. @@ -25,7 +25,7 @@ def test_apply_pending_deposit_under_min_activation(spec, state): @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_min_activation(spec, state): +def test_process_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. @@ -37,7 +37,7 @@ def test_apply_pending_deposit_min_activation(spec, state): @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_over_min_activation(spec, state): +def test_process_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 @@ -49,7 +49,7 @@ def test_apply_pending_deposit_over_min_activation(spec, state): @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_eth1_withdrawal_credentials(spec, state): +def test_process_pending_deposit_eth1_withdrawal_credentials(spec, state): # fresh deposit = next validator index = validator appended to registry validator_index = len(state.validators) withdrawal_credentials = ( @@ -71,7 +71,7 @@ def test_apply_pending_deposit_eth1_withdrawal_credentials(spec, state): @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_compounding_withdrawal_credentials_under_max(spec, state): +def test_process_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 = ( @@ -94,7 +94,7 @@ def test_apply_pending_deposit_compounding_withdrawal_credentials_under_max(spec @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_compounding_withdrawal_credentials_max(spec, state): +def test_process_pending_deposit_compounding_withdrawal_credentials_max(spec, state): # fresh deposit = next validator index = validator appended to registry validator_index = len(state.validators) withdrawal_credentials = ( @@ -117,7 +117,7 @@ def test_apply_pending_deposit_compounding_withdrawal_credentials_max(spec, stat @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_compounding_withdrawal_credentials_over_max(spec, state): +def test_process_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 = ( @@ -140,7 +140,7 @@ def test_apply_pending_deposit_compounding_withdrawal_credentials_over_max(spec, @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_non_versioned_withdrawal_credentials(spec, state): +def test_process_pending_deposit_non_versioned_withdrawal_credentials(spec, state): # fresh deposit = next validator index = validator appended to registry validator_index = len(state.validators) withdrawal_credentials = ( @@ -161,7 +161,7 @@ def test_apply_pending_deposit_non_versioned_withdrawal_credentials(spec, state) @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_non_versioned_withdrawal_credentials_over_min_activation(spec, state): +def test_process_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 = ( @@ -184,7 +184,7 @@ def test_apply_pending_deposit_non_versioned_withdrawal_credentials_over_min_act @with_electra_and_later @spec_state_test @always_bls -def test_apply_pending_deposit_correct_sig_but_forked_state(spec, state): +def test_process_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 @@ -196,7 +196,7 @@ def test_apply_pending_deposit_correct_sig_but_forked_state(spec, state): @with_electra_and_later @spec_state_test @always_bls -def test_apply_pending_deposit_incorrect_sig_new_deposit(spec, state): +def test_process_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 @@ -206,7 +206,7 @@ def test_apply_pending_deposit_incorrect_sig_new_deposit(spec, state): @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_top_up__min_activation_balance(spec, state): +def test_process_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) @@ -222,7 +222,7 @@ def test_apply_pending_deposit_top_up__min_activation_balance(spec, state): @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_top_up__min_activation_balance_compounding(spec, state): +def test_process_pending_deposit_top_up__min_activation_balance_compounding(spec, state): validator_index = 0 withdrawal_credentials = ( spec.COMPOUNDING_WITHDRAWAL_PREFIX @@ -244,7 +244,7 @@ def test_apply_pending_deposit_top_up__min_activation_balance_compounding(spec, @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_top_up__max_effective_balance_compounding(spec, state): +def test_process_pending_deposit_top_up__max_effective_balance_compounding(spec, state): validator_index = 0 withdrawal_credentials = ( spec.COMPOUNDING_WITHDRAWAL_PREFIX @@ -266,7 +266,7 @@ def test_apply_pending_deposit_top_up__max_effective_balance_compounding(spec, s @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_top_up__less_effective_balance(spec, state): +def test_process_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) @@ -285,7 +285,7 @@ def test_apply_pending_deposit_top_up__less_effective_balance(spec, state): @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_top_up__zero_balance(spec, state): +def test_process_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) @@ -305,7 +305,7 @@ def test_apply_pending_deposit_top_up__zero_balance(spec, state): @with_electra_and_later @spec_state_test @always_bls -def test_apply_pending_deposit_incorrect_sig_top_up(spec, state): +def test_process_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) @@ -316,7 +316,7 @@ def test_apply_pending_deposit_incorrect_sig_top_up(spec, state): @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_incorrect_withdrawal_credentials_top_up(spec, state): +def test_process_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:] @@ -335,7 +335,7 @@ def test_apply_pending_deposit_incorrect_withdrawal_credentials_top_up(spec, sta @with_electra_and_later @spec_state_test @always_bls -def test_apply_pending_deposit_key_validate_invalid_subgroup(spec, state): +def test_process_pending_deposit_key_validate_invalid_subgroup(spec, state): validator_index = len(state.validators) amount = spec.MIN_ACTIVATION_BALANCE @@ -350,7 +350,7 @@ def test_apply_pending_deposit_key_validate_invalid_subgroup(spec, state): @with_electra_and_later @spec_state_test @always_bls -def test_apply_pending_deposit_key_validate_invalid_decompression(spec, state): +def test_process_pending_deposit_key_validate_invalid_decompression(spec, state): validator_index = len(state.validators) amount = spec.MIN_ACTIVATION_BALANCE @@ -367,7 +367,7 @@ def test_apply_pending_deposit_key_validate_invalid_decompression(spec, state): @with_electra_and_later @spec_state_test @always_bls -def test_apply_pending_deposit_ineffective_deposit_with_bad_fork_version(spec, state): +def test_process_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( @@ -384,7 +384,7 @@ def test_apply_pending_deposit_ineffective_deposit_with_bad_fork_version(spec, s @with_electra_and_later @spec_state_test @always_bls -def test_apply_pending_deposit_with_previous_fork_version(spec, state): +def test_process_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. @@ -423,7 +423,7 @@ def test_ineffective_deposit_with_current_fork_version(spec, state): @with_electra_and_later @spec_state_test @always_bls -def test_apply_pending_deposit_effective_deposit_with_genesis_fork_version(spec, state): +def test_process_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) @@ -441,7 +441,7 @@ def test_apply_pending_deposit_effective_deposit_with_genesis_fork_version(spec, @with_electra_and_later @spec_state_test -def test_apply_pending_deposit_success_top_up_to_withdrawn_validator(spec, state): +def test_process_pending_deposit_success_top_up_to_withdrawn_validator(spec, state): validator_index = 0 # Fully withdraw validator From ad4227344092254fdbce2c36e3bad9daf8ce2803 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Fri, 4 Oct 2024 11:58:58 -0500 Subject: [PATCH 103/103] Use pending_deposits format for both test files --- .../pending_deposits/__init__.py | 3 ++ .../test_apply_pending_deposit.py} | 48 +++++++++---------- .../test_process_pending_deposits.py | 0 tests/formats/epoch_processing/README.md | 30 ++++++------ tests/generators/epoch_processing/main.py | 9 ++-- 5 files changed, 49 insertions(+), 41 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/electra/epoch_processing/pending_deposits/__init__.py rename tests/core/pyspec/eth2spec/test/electra/epoch_processing/{test_process_pending_deposit.py => pending_deposits/test_apply_pending_deposit.py} (89%) rename tests/core/pyspec/eth2spec/test/electra/epoch_processing/{ => pending_deposits}/test_process_pending_deposits.py (100%) diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/pending_deposits/__init__.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/pending_deposits/__init__.py new file mode 100644 index 0000000000..de7612a767 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/pending_deposits/__init__.py @@ -0,0 +1,3 @@ +# This is a trick to allow tests be split into multiple files and use the same test format. +from .test_apply_pending_deposit import * # noqa: F401 F403 +from .test_process_pending_deposits import * # noqa: F401 F403 diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposit.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/pending_deposits/test_apply_pending_deposit.py similarity index 89% rename from tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposit.py rename to tests/core/pyspec/eth2spec/test/electra/epoch_processing/pending_deposits/test_apply_pending_deposit.py index ad9a70f3a7..0c920d56a3 100644 --- a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposit.py +++ b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/pending_deposits/test_apply_pending_deposit.py @@ -13,7 +13,7 @@ @with_electra_and_later @spec_state_test -def test_process_pending_deposit_under_min_activation(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. @@ -25,7 +25,7 @@ def test_process_pending_deposit_under_min_activation(spec, state): @with_electra_and_later @spec_state_test -def test_process_pending_deposit_min_activation(spec, state): +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. @@ -37,7 +37,7 @@ def test_process_pending_deposit_min_activation(spec, state): @with_electra_and_later @spec_state_test -def test_process_pending_deposit_over_min_activation(spec, state): +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 @@ -49,7 +49,7 @@ def test_process_pending_deposit_over_min_activation(spec, state): @with_electra_and_later @spec_state_test -def test_process_pending_deposit_eth1_withdrawal_credentials(spec, state): +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 = ( @@ -71,7 +71,7 @@ def test_process_pending_deposit_eth1_withdrawal_credentials(spec, state): @with_electra_and_later @spec_state_test -def test_process_pending_deposit_compounding_withdrawal_credentials_under_max(spec, state): +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 = ( @@ -94,7 +94,7 @@ def test_process_pending_deposit_compounding_withdrawal_credentials_under_max(sp @with_electra_and_later @spec_state_test -def test_process_pending_deposit_compounding_withdrawal_credentials_max(spec, state): +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 = ( @@ -117,7 +117,7 @@ def test_process_pending_deposit_compounding_withdrawal_credentials_max(spec, st @with_electra_and_later @spec_state_test -def test_process_pending_deposit_compounding_withdrawal_credentials_over_max(spec, state): +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 = ( @@ -140,7 +140,7 @@ def test_process_pending_deposit_compounding_withdrawal_credentials_over_max(spe @with_electra_and_later @spec_state_test -def test_process_pending_deposit_non_versioned_withdrawal_credentials(spec, state): +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 = ( @@ -161,7 +161,7 @@ def test_process_pending_deposit_non_versioned_withdrawal_credentials(spec, stat @with_electra_and_later @spec_state_test -def test_process_pending_deposit_non_versioned_withdrawal_credentials_over_min_activation(spec, state): +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 = ( @@ -184,7 +184,7 @@ def test_process_pending_deposit_non_versioned_withdrawal_credentials_over_min_a @with_electra_and_later @spec_state_test @always_bls -def test_process_pending_deposit_correct_sig_but_forked_state(spec, state): +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 @@ -196,7 +196,7 @@ def test_process_pending_deposit_correct_sig_but_forked_state(spec, state): @with_electra_and_later @spec_state_test @always_bls -def test_process_pending_deposit_incorrect_sig_new_deposit(spec, state): +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 @@ -206,7 +206,7 @@ def test_process_pending_deposit_incorrect_sig_new_deposit(spec, state): @with_electra_and_later @spec_state_test -def test_process_pending_deposit_top_up__min_activation_balance(spec, state): +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) @@ -222,7 +222,7 @@ def test_process_pending_deposit_top_up__min_activation_balance(spec, state): @with_electra_and_later @spec_state_test -def test_process_pending_deposit_top_up__min_activation_balance_compounding(spec, state): +def test_apply_pending_deposit_top_up__min_activation_balance_compounding(spec, state): validator_index = 0 withdrawal_credentials = ( spec.COMPOUNDING_WITHDRAWAL_PREFIX @@ -244,7 +244,7 @@ def test_process_pending_deposit_top_up__min_activation_balance_compounding(spec @with_electra_and_later @spec_state_test -def test_process_pending_deposit_top_up__max_effective_balance_compounding(spec, state): +def test_apply_pending_deposit_top_up__max_effective_balance_compounding(spec, state): validator_index = 0 withdrawal_credentials = ( spec.COMPOUNDING_WITHDRAWAL_PREFIX @@ -266,7 +266,7 @@ def test_process_pending_deposit_top_up__max_effective_balance_compounding(spec, @with_electra_and_later @spec_state_test -def test_process_pending_deposit_top_up__less_effective_balance(spec, state): +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) @@ -285,7 +285,7 @@ def test_process_pending_deposit_top_up__less_effective_balance(spec, state): @with_electra_and_later @spec_state_test -def test_process_pending_deposit_top_up__zero_balance(spec, state): +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) @@ -305,7 +305,7 @@ def test_process_pending_deposit_top_up__zero_balance(spec, state): @with_electra_and_later @spec_state_test @always_bls -def test_process_pending_deposit_incorrect_sig_top_up(spec, state): +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) @@ -316,7 +316,7 @@ def test_process_pending_deposit_incorrect_sig_top_up(spec, state): @with_electra_and_later @spec_state_test -def test_process_pending_deposit_incorrect_withdrawal_credentials_top_up(spec, state): +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:] @@ -335,7 +335,7 @@ def test_process_pending_deposit_incorrect_withdrawal_credentials_top_up(spec, s @with_electra_and_later @spec_state_test @always_bls -def test_process_pending_deposit_key_validate_invalid_subgroup(spec, state): +def test_apply_pending_deposit_key_validate_invalid_subgroup(spec, state): validator_index = len(state.validators) amount = spec.MIN_ACTIVATION_BALANCE @@ -350,7 +350,7 @@ def test_process_pending_deposit_key_validate_invalid_subgroup(spec, state): @with_electra_and_later @spec_state_test @always_bls -def test_process_pending_deposit_key_validate_invalid_decompression(spec, state): +def test_apply_pending_deposit_key_validate_invalid_decompression(spec, state): validator_index = len(state.validators) amount = spec.MIN_ACTIVATION_BALANCE @@ -367,7 +367,7 @@ def test_process_pending_deposit_key_validate_invalid_decompression(spec, state) @with_electra_and_later @spec_state_test @always_bls -def test_process_pending_deposit_ineffective_deposit_with_bad_fork_version(spec, state): +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( @@ -384,7 +384,7 @@ def test_process_pending_deposit_ineffective_deposit_with_bad_fork_version(spec, @with_electra_and_later @spec_state_test @always_bls -def test_process_pending_deposit_with_previous_fork_version(spec, state): +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. @@ -423,7 +423,7 @@ def test_ineffective_deposit_with_current_fork_version(spec, state): @with_electra_and_later @spec_state_test @always_bls -def test_process_pending_deposit_effective_deposit_with_genesis_fork_version(spec, state): +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) @@ -441,7 +441,7 @@ def test_process_pending_deposit_effective_deposit_with_genesis_fork_version(spe @with_electra_and_later @spec_state_test -def test_process_pending_deposit_success_top_up_to_withdrawn_validator(spec, state): +def test_apply_pending_deposit_success_top_up_to_withdrawn_validator(spec, state): validator_index = 0 # Fully withdraw validator diff --git a/tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py b/tests/core/pyspec/eth2spec/test/electra/epoch_processing/pending_deposits/test_process_pending_deposits.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/electra/epoch_processing/test_process_pending_deposits.py rename to tests/core/pyspec/eth2spec/test/electra/epoch_processing/pending_deposits/test_process_pending_deposits.py diff --git a/tests/formats/epoch_processing/README.md b/tests/formats/epoch_processing/README.md index 2951767f2c..0f6642141a 100644 --- a/tests/formats/epoch_processing/README.md +++ b/tests/formats/epoch_processing/README.md @@ -32,19 +32,21 @@ The provided pre-state is already transitioned to just before the specific sub-t Sub-transitions: -- `justification_and_finalization` -- `inactivity_updates` (Altair) -- `rewards_and_penalties` -- `registry_updates` -- `slashings` -- `eth1_data_reset` -- `effective_balance_updates` -- `slashings_reset` -- `randao_mixes_reset` -- `historical_roots_update` (Phase0, Altair, Bellatrix only) -- `historical_summaries_update` (Capella) -- `participation_record_updates` (Phase 0 only) -- `participation_flag_updates` (Altair) -- `sync_committee_updates` (Altair) +- `eth1_data_reset` (>=Phase0) +- `historical_roots_update` (>=Phase0,<=Bellatrix) +- `justification_and_finalization` (>=Phase0) +- `participation_record_updates` (==Phase0) +- `randao_mixes_reset` (>=Phase0) +- `registry_updates` (>=Phase0) +- `rewards_and_penalties` (>=Phase0) +- `slashings_reset` (>=Phase0) +- `slashings` (>=Phase0) +- `inactivity_updates` (>=Altair) +- `participation_flag_updates` (>=Altair) +- `sync_committee_updates` (>=Altair) +- `historical_summaries_update` (>=Capella) +- `effective_balance_updates` (>=Electra) +- `pending_consolidations` (>=Electra) +- `pending_deposits` (>=Electra) The resulting state should match the expected `post` state. diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index c420da2e93..1de3e84e3d 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -37,12 +37,15 @@ ]} deneb_mods = combine_mods(_new_deneb_mods, capella_mods) - _new_electra_mods = {key: 'eth2spec.test.electra.epoch_processing.test_process_' + key for key in [ + _new_electra_mods_1 = {key: 'eth2spec.test.electra.epoch_processing.test_process_' + key for key in [ 'effective_balance_updates', - 'pending_deposit', - 'pending_deposits', 'pending_consolidations', ]} + # This is a trick to allow tests be split into multiple files and use the same test format. + _new_electra_mods_2 = {key: 'eth2spec.test.electra.epoch_processing.' + key for key in [ + 'pending_deposits', + ]} + _new_electra_mods = {**_new_electra_mods_1, **_new_electra_mods_2} electra_mods = combine_mods(_new_electra_mods, deneb_mods) # TODO Custody Game testgen is disabled for now