Merge remote-tracking branch 'upstream/deposit-queue' into deposit-queue

This commit is contained in:
james-prysm 2024-07-08 16:44:25 -05:00
commit 093590ee11
5 changed files with 406 additions and 132 deletions

View File

@ -22,6 +22,7 @@
- [`_fft_field`](#_fft_field) - [`_fft_field`](#_fft_field)
- [`fft_field`](#fft_field) - [`fft_field`](#fft_field)
- [`coset_fft_field`](#coset_fft_field) - [`coset_fft_field`](#coset_fft_field)
- [`compute_verify_cell_kzg_proof_batch_challenge`](#compute_verify_cell_kzg_proof_batch_challenge)
- [Polynomials in coefficient form](#polynomials-in-coefficient-form) - [Polynomials in coefficient form](#polynomials-in-coefficient-form)
- [`polynomial_eval_to_coeff`](#polynomial_eval_to_coeff) - [`polynomial_eval_to_coeff`](#polynomial_eval_to_coeff)
- [`add_polynomialcoeff`](#add_polynomialcoeff) - [`add_polynomialcoeff`](#add_polynomialcoeff)
@ -34,7 +35,9 @@
- [KZG multiproofs](#kzg-multiproofs) - [KZG multiproofs](#kzg-multiproofs)
- [`compute_kzg_proof_multi_impl`](#compute_kzg_proof_multi_impl) - [`compute_kzg_proof_multi_impl`](#compute_kzg_proof_multi_impl)
- [`verify_kzg_proof_multi_impl`](#verify_kzg_proof_multi_impl) - [`verify_kzg_proof_multi_impl`](#verify_kzg_proof_multi_impl)
- [`verify_cell_kzg_proof_batch_impl`](#verify_cell_kzg_proof_batch_impl)
- [Cell cosets](#cell-cosets) - [Cell cosets](#cell-cosets)
- [`coset_shift_for_cell`](#coset_shift_for_cell)
- [`coset_for_cell`](#coset_for_cell) - [`coset_for_cell`](#coset_for_cell)
- [Cells](#cells-1) - [Cells](#cells-1)
- [Cell computation](#cell-computation) - [Cell computation](#cell-computation)
@ -222,6 +225,52 @@ def coset_fft_field(vals: Sequence[BLSFieldElement],
return fft_field(vals, roots_of_unity, inv) return fft_field(vals, roots_of_unity, inv)
``` ```
#### `compute_verify_cell_kzg_proof_batch_challenge`
```python
def compute_verify_cell_kzg_proof_batch_challenge(row_commitments: Sequence[KZGCommitment],
row_indices: Sequence[RowIndex],
column_indices: Sequence[ColumnIndex],
cosets_evals: Sequence[CosetEvals],
proofs: Sequence[KZGProof]) -> BLSFieldElement:
"""
Compute a random challenge r used in the universal verification equation.
This is used in verify_cell_kzg_proof_batch_impl.
To compute the challenge, `RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN` is used as a hash prefix.
"""
# input the domain separator
hashinput = RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN
# input the degree bound of the polynomial
hashinput += int.to_bytes(FIELD_ELEMENTS_PER_BLOB, 8, KZG_ENDIANNESS)
# input the field elements per cell
hashinput += int.to_bytes(FIELD_ELEMENTS_PER_CELL, 8, KZG_ENDIANNESS)
# input the number of commitments
num_commitments = len(row_commitments)
hashinput += int.to_bytes(num_commitments, 8, KZG_ENDIANNESS)
# input the number of cells
num_cells = len(row_indices)
hashinput += int.to_bytes(num_cells, 8, KZG_ENDIANNESS)
# input all commitments
for commitment in row_commitments:
hashinput += commitment
# input each cell with its indices and proof
for k in range(num_cells):
hashinput += int.to_bytes(row_indices[k], 8, KZG_ENDIANNESS)
hashinput += int.to_bytes(column_indices[k], 8, KZG_ENDIANNESS)
for eval in cosets_evals[k]:
hashinput += bls_field_to_bytes(eval)
hashinput += proofs[k]
return hash_to_bls_field(hashinput)
```
### Polynomials in coefficient form ### Polynomials in coefficient form
#### `polynomial_eval_to_coeff` #### `polynomial_eval_to_coeff`
@ -423,14 +472,132 @@ def verify_kzg_proof_multi_impl(commitment: KZGCommitment,
])) ]))
``` ```
#### `verify_cell_kzg_proof_batch_impl`
```python
def verify_cell_kzg_proof_batch_impl(row_commitments: Sequence[KZGCommitment],
row_indices: Sequence[RowIndex],
column_indices: Sequence[ColumnIndex],
cosets_evals: Sequence[CosetEvals],
proofs: Sequence[KZGProof]) -> bool:
"""
Verify a set of cells, given their corresponding proofs and their coordinates (row_index, column_index) in the blob
matrix. The i-th cell is in row row_indices[i] and in column column_indices[i].
The list of all commitments is provided in row_commitments_bytes.
This function is the internal implementation of verify_cell_kzg_proof_batch.
"""
# The verification equation that we will check is pairing (LL, LR) = pairing (RL, [1]), where
# LL = sum_k r^k proofs[k],
# LR = [s^n]
# RL = RLC - RLI + RLP, where
# RLC = sum_i weights[i] commitments[i]
# RLI = [sum_k r^k interpolation_poly_k(s)]
# RLP = sum_k (r^k * h_k^n) proofs[k]
#
# Here, the variables have the following meaning:
# - k < len(row_indices) is an index iterating over all cells in the input
# - r is a random coefficient, derived from hashing all data provided by the prover
# - s is the secret embedded in the KZG setup
# - n = FIELD_ELEMENTS_PER_CELL is the size of the evaluation domain
# - i ranges over all rows that are touched
# - weights[i] is a weight computed for row i. It depends on r and on which cells are in row i
# - interpolation_poly_k is the interpolation polynomial for the kth cell
# - h_k is the coset shift specifying the evaluation domain of the kth cell
# Preparation
num_cells = len(row_indices)
n = FIELD_ELEMENTS_PER_CELL
num_rows = len(row_commitments)
# Step 1: Compute a challenge r and its powers r^0, ..., r^{num_cells-1}
r = compute_verify_cell_kzg_proof_batch_challenge(
row_commitments,
row_indices,
column_indices,
cosets_evals,
proofs
)
r_powers = compute_powers(r, num_cells)
# Step 2: Compute LL = sum_k r^k proofs[k]
ll = bls.bytes48_to_G1(g1_lincomb(proofs, r_powers))
# Step 3: Compute LR = [s^n]
lr = bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[n])
# Step 4: Compute RL = RLC - RLI + RLP
# 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 in row i.
# Note: we do that by iterating over all k and updating the correct weights[i] accordingly
weights = [0] * num_rows
for k in range(num_cells):
i = row_indices[k]
weights[i] = (weights[i] + int(r_powers[k])) % BLS_MODULUS
# Step 4.1b: Linearly combine the weights with the commitments to get RLC
rlc = bls.bytes48_to_G1(g1_lincomb(row_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]
for k in range(num_cells):
interp_poly_coeff = interpolate_polynomialcoeff(coset_for_cell(column_indices[k]), cosets_evals[k])
interp_poly_scaled_coeff = multiply_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(column_indices[k]))
h_k_pow = pow(h_k, n, BLS_MODULUS)
wrp = (int(r_powers[k]) * h_k_pow) % BLS_MODULUS
weighted_r_powers.append(wrp)
rlp = bls.bytes48_to_G1(g1_lincomb(proofs, weighted_r_powers))
# Step 4.4: Compute RL = RLC - RLI + RLP
rl = bls.add(rlc, bls.neg(rli))
rl = bls.add(rl, rlp)
# Step 5: Check pairing (LL, LR) = pairing (RL, [1])
return (bls.pairing_check([
[ll, lr],
[rl, bls.neg(bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[0]))],
]))
```
### Cell cosets ### Cell cosets
#### `coset_shift_for_cell`
```python
def coset_shift_for_cell(cell_index: CellIndex) -> BLSFieldElement:
"""
Get the shift that determines the coset for a given ``cell_index``.
Precisely, consider the group of roots of unity of order FIELD_ELEMENTS_PER_CELL * CELLS_PER_EXT_BLOB.
Let G = {1, g, g^2, ...} denote its subgroup of order FIELD_ELEMENTS_PER_CELL.
Then, the coset is defined as h * G = {h, hg, hg^2, ...} for an element h.
This function returns h.
"""
assert cell_index < CELLS_PER_EXT_BLOB
roots_of_unity_brp = bit_reversal_permutation(
compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB)
)
return roots_of_unity_brp[FIELD_ELEMENTS_PER_CELL * cell_index]
```
#### `coset_for_cell` #### `coset_for_cell`
```python ```python
def coset_for_cell(cell_index: CellIndex) -> Coset: def coset_for_cell(cell_index: CellIndex) -> Coset:
""" """
Get the coset for a given ``cell_index``. Get the coset for a given ``cell_index``.
Precisely, consider the group of roots of unity of order FIELD_ELEMENTS_PER_CELL * CELLS_PER_EXT_BLOB.
Let G = {1, g, g^2, ...} denote its subgroup of order FIELD_ELEMENTS_PER_CELL.
Then, the coset is defined as h * G = {h, hg, hg^2, ...}.
This function, returns the coset.
""" """
assert cell_index < CELLS_PER_EXT_BLOB assert cell_index < CELLS_PER_EXT_BLOB
roots_of_unity_brp = bit_reversal_permutation( roots_of_unity_brp = bit_reversal_permutation(
@ -511,18 +678,15 @@ def verify_cell_kzg_proof_batch(row_commitments_bytes: Sequence[Bytes48],
proofs_bytes: Sequence[Bytes48]) -> bool: proofs_bytes: Sequence[Bytes48]) -> bool:
""" """
Verify a set of cells, given their corresponding proofs and their coordinates (row_index, column_index) in the blob Verify a set of cells, given their corresponding proofs and their coordinates (row_index, column_index) in the blob
matrix. The list of all commitments is also provided in row_commitments_bytes. matrix. The i-th cell is in row = row_indices[i] and in column = column_indices[i].
The list of all commitments is provided in row_commitments_bytes.
This function implements the naive algorithm of checking every cell This function implements the universal verification equation that has been introduced here:
individually; an efficient algorithm can be found here:
https://ethresear.ch/t/a-universal-verification-equation-for-data-availability-sampling/13240 https://ethresear.ch/t/a-universal-verification-equation-for-data-availability-sampling/13240
This implementation does not require randomness, but for the algorithm that
requires it, `RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN` should be used to compute
the challenge value.
Public method. Public method.
""" """
assert len(cells) == len(proofs_bytes) == len(row_indices) == len(column_indices) assert len(cells) == len(proofs_bytes) == len(row_indices) == len(column_indices)
for commitment_bytes in row_commitments_bytes: for commitment_bytes in row_commitments_bytes:
assert len(commitment_bytes) == BYTES_PER_COMMITMENT assert len(commitment_bytes) == BYTES_PER_COMMITMENT
@ -535,18 +699,13 @@ def verify_cell_kzg_proof_batch(row_commitments_bytes: Sequence[Bytes48],
for proof_bytes in proofs_bytes: for proof_bytes in proofs_bytes:
assert len(proof_bytes) == BYTES_PER_PROOF assert len(proof_bytes) == BYTES_PER_PROOF
# Get commitments via row indices
commitments_bytes = [row_commitments_bytes[row_index] for row_index in row_indices]
# Get objects from bytes # Get objects from bytes
commitments = [bytes_to_kzg_commitment(commitment_bytes) for commitment_bytes in commitments_bytes] row_commitments = [bytes_to_kzg_commitment(commitment_bytes) for commitment_bytes in row_commitments_bytes]
cosets_evals = [cell_to_coset_evals(cell) for cell in cells] cosets_evals = [cell_to_coset_evals(cell) for cell in cells]
proofs = [bytes_to_kzg_proof(proof_bytes) for proof_bytes in proofs_bytes] proofs = [bytes_to_kzg_proof(proof_bytes) for proof_bytes in proofs_bytes]
return all( # Do the actual verification
verify_kzg_proof_multi_impl(commitment, coset_for_cell(column_index), coset_evals, proof) return verify_cell_kzg_proof_batch_impl(row_commitments, row_indices, column_indices, cosets_evals, proofs)
for commitment, column_index, coset_evals, proof in zip(commitments, column_indices, cosets_evals, proofs)
)
``` ```
## Reconstruction ## Reconstruction

View File

@ -44,16 +44,16 @@
- [`BeaconState`](#beaconstate) - [`BeaconState`](#beaconstate)
- [Helper functions](#helper-functions) - [Helper functions](#helper-functions)
- [Predicates](#predicates) - [Predicates](#predicates)
- [Updated `compute_proposer_index`](#updated-compute_proposer_index) - [Modified `compute_proposer_index`](#modified-compute_proposer_index)
- [Updated `is_eligible_for_activation_queue`](#updated-is_eligible_for_activation_queue) - [Modified `is_eligible_for_activation_queue`](#modified-is_eligible_for_activation_queue)
- [New `is_compounding_withdrawal_credential`](#new-is_compounding_withdrawal_credential) - [New `is_compounding_withdrawal_credential`](#new-is_compounding_withdrawal_credential)
- [New `has_compounding_withdrawal_credential`](#new-has_compounding_withdrawal_credential) - [New `has_compounding_withdrawal_credential`](#new-has_compounding_withdrawal_credential)
- [New `has_execution_withdrawal_credential`](#new-has_execution_withdrawal_credential) - [New `has_execution_withdrawal_credential`](#new-has_execution_withdrawal_credential)
- [Updated `is_fully_withdrawable_validator`](#updated-is_fully_withdrawable_validator) - [Modified `is_fully_withdrawable_validator`](#modified-is_fully_withdrawable_validator)
- [Updated `is_partially_withdrawable_validator`](#updated-is_partially_withdrawable_validator) - [Modified `is_partially_withdrawable_validator`](#modified-is_partially_withdrawable_validator)
- [Misc](#misc-1) - [Misc](#misc-1)
- [`get_committee_indices`](#get_committee_indices) - [New `get_committee_indices`](#new-get_committee_indices)
- [`get_validator_max_effective_balance`](#get_validator_max_effective_balance) - [New `get_validator_max_effective_balance`](#new-get_validator_max_effective_balance)
- [Beacon state accessors](#beacon-state-accessors) - [Beacon state accessors](#beacon-state-accessors)
- [New `get_balance_churn_limit`](#new-get_balance_churn_limit) - [New `get_balance_churn_limit`](#new-get_balance_churn_limit)
- [New `get_activation_exit_churn_limit`](#new-get_activation_exit_churn_limit) - [New `get_activation_exit_churn_limit`](#new-get_activation_exit_churn_limit)
@ -61,28 +61,27 @@
- [New `get_active_balance`](#new-get_active_balance) - [New `get_active_balance`](#new-get_active_balance)
- [New `get_pending_balance_to_withdraw`](#new-get_pending_balance_to_withdraw) - [New `get_pending_balance_to_withdraw`](#new-get_pending_balance_to_withdraw)
- [Modified `get_attesting_indices`](#modified-get_attesting_indices) - [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) - [Modified `get_next_sync_committee_indices`](#modified-get_next_sync_committee_indices)
- [Beacon state mutators](#beacon-state-mutators) - [Beacon state mutators](#beacon-state-mutators)
- [Updated `initiate_validator_exit`](#updated--initiate_validator_exit) - [Modified `initiate_validator_exit`](#modified-initiate_validator_exit)
- [New `switch_to_compounding_validator`](#new-switch_to_compounding_validator) - [New `switch_to_compounding_validator`](#new-switch_to_compounding_validator)
- [New `queue_excess_active_balance`](#new-queue_excess_active_balance) - [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 `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_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) - [New `compute_consolidation_epoch_and_update_churn`](#new-compute_consolidation_epoch_and_update_churn)
- [Updated `slash_validator`](#updated-slash_validator) - [Modified `slash_validator`](#modified-slash_validator)
- [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Epoch processing](#epoch-processing) - [Epoch processing](#epoch-processing)
- [Updated `process_epoch`](#updated-process_epoch) - [Modified `process_epoch`](#modified-process_epoch)
- [Updated `process_registry_updates`](#updated--process_registry_updates) - [Modified `process_registry_updates`](#modified-process_registry_updates)
- [New `apply_pending_deposit`](#new-apply_pending_deposit) - [New `apply_pending_deposit`](#new-apply_pending_deposit)
- [New `process_pending_deposits`](#new-process_pending_deposits) - [New `process_pending_deposits`](#new-process_pending_deposits)
- [New `process_pending_consolidations`](#new-process_pending_consolidations) - [New `process_pending_consolidations`](#new-process_pending_consolidations)
- [Updated `process_effective_balance_updates`](#updated-process_effective_balance_updates) - [Modified `process_effective_balance_updates`](#modified-process_effective_balance_updates)
- [Block processing](#block-processing) - [Block processing](#block-processing)
- [Withdrawals](#withdrawals) - [Withdrawals](#withdrawals)
- [Updated `get_expected_withdrawals`](#updated-get_expected_withdrawals) - [Modified `get_expected_withdrawals`](#modified-get_expected_withdrawals)
- [Updated `process_withdrawals`](#updated-process_withdrawals) - [Modified `process_withdrawals`](#modified-process_withdrawals)
- [Execution payload](#execution-payload) - [Execution payload](#execution-payload)
- [Modified `process_execution_payload`](#modified-process_execution_payload) - [Modified `process_execution_payload`](#modified-process_execution_payload)
- [Operations](#operations) - [Operations](#operations)
@ -90,10 +89,10 @@
- [Attestations](#attestations) - [Attestations](#attestations)
- [Modified `process_attestation`](#modified-process_attestation) - [Modified `process_attestation`](#modified-process_attestation)
- [Deposits](#deposits) - [Deposits](#deposits)
- [Updated `apply_deposit`](#updated--apply_deposit) - [Modified `apply_deposit`](#modified-apply_deposit)
- [New `is_valid_deposit_signature`](#new-is_valid_deposit_signature) - [New `is_valid_deposit_signature`](#new-is_valid_deposit_signature)
- [Voluntary exits](#voluntary-exits) - [Voluntary exits](#voluntary-exits)
- [Updated `process_voluntary_exit`](#updated-process_voluntary_exit) - [Modified `process_voluntary_exit`](#modified-process_voluntary_exit)
- [Execution layer withdrawal requests](#execution-layer-withdrawal-requests) - [Execution layer withdrawal requests](#execution-layer-withdrawal-requests)
- [New `process_withdrawal_request`](#new-process_withdrawal_request) - [New `process_withdrawal_request`](#new-process_withdrawal_request)
- [Deposit requests](#deposit-requests) - [Deposit requests](#deposit-requests)
@ -113,7 +112,7 @@ Electra is a consensus-layer upgrade containing a number of features. Including:
* [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251): Increase the MAX_EFFECTIVE_BALANCE * [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251): Increase the MAX_EFFECTIVE_BALANCE
* [EIP-7549](https://eips.ethereum.org/EIPS/eip-7549): Move committee index outside Attestation * [EIP-7549](https://eips.ethereum.org/EIPS/eip-7549): Move committee index outside Attestation
*Note:* This specification is built upon [Deneb](../../deneb/beacon_chain.md) and is under active development. *Note:* This specification is built upon [Deneb](../deneb/beacon_chain.md) and is under active development.
## Constants ## Constants
@ -440,9 +439,9 @@ class BeaconState(Container):
### Predicates ### Predicates
#### Updated `compute_proposer_index` #### Modified `compute_proposer_index`
*Note*: The function is modified to use `MAX_EFFECTIVE_BALANCE_ELECTRA` preset. *Note*: The function `compute_proposer_index` is modified to use `MAX_EFFECTIVE_BALANCE_ELECTRA`.
```python ```python
def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32) -> ValidatorIndex: def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32) -> ValidatorIndex:
@ -463,7 +462,9 @@ def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex]
i += 1 i += 1
``` ```
#### Updated `is_eligible_for_activation_queue` #### Modified `is_eligible_for_activation_queue`
*Note*: The function `is_eligible_for_activation_queue` is modified to use `MIN_ACTIVATION_BALANCE` instead of `MAX_EFFECTIVE_BALANCE`.
```python ```python
def is_eligible_for_activation_queue(validator: Validator) -> bool: def is_eligible_for_activation_queue(validator: Validator) -> bool:
@ -503,7 +504,9 @@ def has_execution_withdrawal_credential(validator: Validator) -> bool:
return has_compounding_withdrawal_credential(validator) or has_eth1_withdrawal_credential(validator) return has_compounding_withdrawal_credential(validator) or has_eth1_withdrawal_credential(validator)
``` ```
#### Updated `is_fully_withdrawable_validator` #### Modified `is_fully_withdrawable_validator`
*Note*: The function `is_fully_withdrawable_validator` is modified to use `has_execution_withdrawal_credential` instead of `has_eth1_withdrawal_credential`.
```python ```python
def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch: Epoch) -> bool: def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch: Epoch) -> bool:
@ -517,7 +520,9 @@ def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch:
) )
``` ```
#### Updated `is_partially_withdrawable_validator` #### Modified `is_partially_withdrawable_validator`
*Note*: The function `is_partially_withdrawable_validator` is modified to use `get_validator_max_effective_balance` instead of `MAX_EFFECTIVE_BALANCE` and `has_execution_withdrawal_credential` instead of `has_eth1_withdrawal_credential`.
```python ```python
def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> bool: def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> bool:
@ -536,14 +541,14 @@ def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) ->
### Misc ### Misc
#### `get_committee_indices` #### New `get_committee_indices`
```python ```python
def get_committee_indices(committee_bits: Bitvector) -> Sequence[CommitteeIndex]: def get_committee_indices(committee_bits: Bitvector) -> Sequence[CommitteeIndex]:
return [CommitteeIndex(index) for index, bit in enumerate(committee_bits) if bit] return [CommitteeIndex(index) for index, bit in enumerate(committee_bits) if bit]
``` ```
#### `get_validator_max_effective_balance` #### New `get_validator_max_effective_balance`
```python ```python
def get_validator_max_effective_balance(validator: Validator) -> Gwei: def get_validator_max_effective_balance(validator: Validator) -> Gwei:
@ -608,6 +613,8 @@ def get_pending_balance_to_withdraw(state: BeaconState, validator_index: Validat
#### Modified `get_attesting_indices` #### Modified `get_attesting_indices`
*Note*: The function `get_attesting_indices` is modified to support EIP7549.
```python ```python
def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[ValidatorIndex]: def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[ValidatorIndex]:
""" """
@ -627,30 +634,9 @@ def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[V
return output 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` #### Modified `get_next_sync_committee_indices`
*Note*: The function is modified to use `MAX_EFFECTIVE_BALANCE_ELECTRA` preset. *Note*: The function `get_next_sync_committee_indices` is modified to use `MAX_EFFECTIVE_BALANCE_ELECTRA`.
```python ```python
def get_next_sync_committee_indices(state: BeaconState) -> Sequence[ValidatorIndex]: def get_next_sync_committee_indices(state: BeaconState) -> Sequence[ValidatorIndex]:
@ -679,7 +665,9 @@ def get_next_sync_committee_indices(state: BeaconState) -> Sequence[ValidatorInd
### Beacon state mutators ### Beacon state mutators
#### Updated `initiate_validator_exit` #### Modified `initiate_validator_exit`
*Note*: The function `initiate_validator_exit` is modified to use the new `compute_exit_epoch_and_update_churn` function.
```python ```python
def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
@ -728,6 +716,7 @@ def queue_excess_active_balance(state: BeaconState, index: ValidatorIndex) -> No
``` ```
#### New `queue_entire_balance_and_reset_validator` #### New `queue_entire_balance_and_reset_validator`
```python ```python
def queue_entire_balance_and_reset_validator(state: BeaconState, index: ValidatorIndex) -> None: def queue_entire_balance_and_reset_validator(state: BeaconState, index: ValidatorIndex) -> None:
balance = state.balances[index] balance = state.balances[index]
@ -797,7 +786,9 @@ def compute_consolidation_epoch_and_update_churn(state: BeaconState, consolidati
return state.earliest_consolidation_epoch return state.earliest_consolidation_epoch
``` ```
#### Updated `slash_validator` #### Modified `slash_validator`
*Note*: The function `slash_validator` is modified to change how the slashing penalty and proposer/whistleblower rewards are calculated in accordance with EIP7251.
```python ```python
def slash_validator(state: BeaconState, def slash_validator(state: BeaconState,
@ -831,7 +822,10 @@ def slash_validator(state: BeaconState,
### Epoch processing ### Epoch processing
#### Updated `process_epoch` #### Modified `process_epoch`
*Note*: The function `process_epoch` is modified to call updated functions and to process pending balance deposits and pending consolidations which are new in Electra.
```python ```python
def process_epoch(state: BeaconState) -> None: def process_epoch(state: BeaconState) -> None:
process_justification_and_finalization(state) process_justification_and_finalization(state)
@ -850,9 +844,9 @@ def process_epoch(state: BeaconState) -> None:
process_sync_committee_updates(state) process_sync_committee_updates(state)
``` ```
#### Updated `process_registry_updates` #### Modified `process_registry_updates`
`process_registry_updates` uses the updated definition of `initiate_validator_exit` *Note*: The function `process_registry_updates` is modified to use the updated definition of `initiate_validator_exit`
and changes how the activation epochs are computed for eligible validators. and changes how the activation epochs are computed for eligible validators.
```python ```python
@ -878,11 +872,9 @@ def process_registry_updates(state: BeaconState) -> None:
#### New `apply_pending_deposit` #### New `apply_pending_deposit`
```python ```python
def apply_pending_deposit(state: BeaconState, deposit: PendingDeposit) -> bool: def apply_pending_deposit(state: BeaconState, deposit: PendingDeposit) -> None:
""" """
Applies ``deposit`` to the ``state``. 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] validator_pubkeys = [v.pubkey for v in state.validators]
if deposit.pubkey not in validator_pubkeys: if deposit.pubkey not in validator_pubkeys:
@ -896,32 +888,22 @@ def apply_pending_deposit(state: BeaconState, deposit: PendingDeposit) -> bool:
add_validator_to_registry(state, deposit.pubkey, deposit.withdrawal_credentials, deposit.amount) add_validator_to_registry(state, deposit.pubkey, deposit.withdrawal_credentials, deposit.amount)
else: else:
validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey)) 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 = state.validators[validator_index]
# Validator is exiting, postpone the deposit until after withdrawable epoch if (
if validator.exit_epoch < FAR_FUTURE_EPOCH: validator.exit_epoch < get_current_epoch(state)
if get_current_epoch(state) <= validator.withdrawable_epoch: and is_compounding_withdrawal_credential(deposit.withdrawal_credentials)
return False and has_eth1_withdrawal_credential(validator)
# Deposited balance will never become active. Increase balance but do not consume churn and is_valid_deposit_signature(
else: deposit.pubkey,
increase_balance(state, validator_index, deposit.amount) deposit.withdrawal_credentials,
# Validator is not exiting, attempt to process deposit deposit.amount,
else: deposit.signature
# Increase balance )
increase_balance(state, validator_index, deposit.amount) ):
# Check if valid deposit switch to compounding credentials. switch_to_compounding_validator(state, validator_index)
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` #### New `process_pending_deposits`
@ -959,19 +941,30 @@ def process_pending_deposits(state: BeaconState) -> None:
if next_deposit_index >= MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING: if next_deposit_index >= MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING:
break break
# Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. # Read validator state
churn_consumption = get_activation_churn_consumption(state, deposit) is_validator_exited = False
if processed_amount + churn_consumption > available_for_processing: is_validator_withdrawn = False
is_churn_limit_reached = True validator_pubkeys = [v.pubkey for v in state.validators]
break 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)
# Consume churn and apply deposit if is_validator_withdrawn:
processed_amount += churn_consumption # Deposited balance will never become active. Increase balance but do not consume churn
is_deposit_applied = apply_pending_deposit(state, deposit) apply_pending_deposit(state, deposit)
elif is_validator_exited:
# Postpone deposit if it has not been applied. # Validator is exiting, postpone the deposit until after withdrawable epoch
if not is_deposit_applied:
deposits_to_postpone.append(deposit) 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. # Regardless of how the deposit was handled, we move on in the queue.
next_deposit_index += 1 next_deposit_index += 1
@ -1011,9 +1004,9 @@ def process_pending_consolidations(state: BeaconState) -> None:
state.pending_consolidations = state.pending_consolidations[next_pending_consolidation:] state.pending_consolidations = state.pending_consolidations[next_pending_consolidation:]
``` ```
#### Updated `process_effective_balance_updates` #### Modified `process_effective_balance_updates`
`process_effective_balance_updates` is updated with a new limit for the maximum effective balance. *Note*: The function `process_effective_balance_updates` is modified to use the new limit for the maximum effective balance.
```python ```python
def process_effective_balance_updates(state: BeaconState) -> None: def process_effective_balance_updates(state: BeaconState) -> None:
@ -1023,6 +1016,7 @@ def process_effective_balance_updates(state: BeaconState) -> None:
HYSTERESIS_INCREMENT = uint64(EFFECTIVE_BALANCE_INCREMENT // HYSTERESIS_QUOTIENT) HYSTERESIS_INCREMENT = uint64(EFFECTIVE_BALANCE_INCREMENT // HYSTERESIS_QUOTIENT)
DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER
UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER
# [Modified in Electra:EIP7251]
EFFECTIVE_BALANCE_LIMIT = ( EFFECTIVE_BALANCE_LIMIT = (
MAX_EFFECTIVE_BALANCE_ELECTRA if has_compounding_withdrawal_credential(validator) MAX_EFFECTIVE_BALANCE_ELECTRA if has_compounding_withdrawal_credential(validator)
else MIN_ACTIVATION_BALANCE else MIN_ACTIVATION_BALANCE
@ -1050,7 +1044,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
#### Withdrawals #### Withdrawals
##### Updated `get_expected_withdrawals` ##### Modified `get_expected_withdrawals`
*Note*: The function `get_expected_withdrawals` is modified to support EIP7251.
```python ```python
def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], uint64]: def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], uint64]:
@ -1106,7 +1102,9 @@ def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal],
return withdrawals, partial_withdrawals_count return withdrawals, partial_withdrawals_count
``` ```
##### Updated `process_withdrawals` ##### Modified `process_withdrawals`
*Note*: The function `process_withdrawals` is modified to support EIP7251.
```python ```python
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None: def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
@ -1272,9 +1270,9 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
##### Deposits ##### Deposits
###### Updated `apply_deposit` ###### Modified `apply_deposit`
*NOTE*: `process_deposit` is updated with a new definition of `apply_deposit`. *Note*: The function `process_deposit` is modified to support EIP7251.
```python ```python
def apply_deposit(state: BeaconState, def apply_deposit(state: BeaconState,
@ -1297,7 +1295,7 @@ def apply_deposit(state: BeaconState,
)) ))
else: else:
# Increase balance by deposit amount # Increase balance by deposit amount
# [Modified in Electra:EIP-7251] # [Modified in Electra:EIP7251]
state.pending_deposits.append(PendingDeposit( state.pending_deposits.append(PendingDeposit(
pubkey=pubkey, pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials, withdrawal_credentials=withdrawal_credentials,
@ -1325,7 +1323,10 @@ def is_valid_deposit_signature(pubkey: BLSPubkey,
``` ```
##### Voluntary exits ##### Voluntary exits
###### Updated `process_voluntary_exit`
###### Modified `process_voluntary_exit`
*Note*: The function `process_voluntary_exit` is modified to ensure the validator has no pending withdrawals in the queue.
```python ```python
def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None: def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
@ -1353,8 +1354,6 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu
###### New `process_withdrawal_request` ###### New `process_withdrawal_request`
*Note*: This function is new in Electra following EIP-7002 and EIP-7251.
```python ```python
def process_withdrawal_request( def process_withdrawal_request(
state: BeaconState, state: BeaconState,
@ -1422,8 +1421,6 @@ def process_withdrawal_request(
###### New `process_deposit_request` ###### New `process_deposit_request`
*Note*: This function is new in Electra:EIP6110.
```python ```python
def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) -> None: def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) -> None:
# Set deposit request start index # Set deposit request start index

View File

@ -126,6 +126,8 @@ def test_verify_cell_kzg_proof(spec):
@spec_test @spec_test
@single_phase @single_phase
def test_verify_cell_kzg_proof_batch(spec): def test_verify_cell_kzg_proof_batch(spec):
# test with a single blob / commitment
blob = get_sample_blob(spec) blob = get_sample_blob(spec)
commitment = spec.blob_to_kzg_commitment(blob) commitment = spec.blob_to_kzg_commitment(blob)
cells, proofs = spec.compute_cells_and_kzg_proofs(blob) cells, proofs = spec.compute_cells_and_kzg_proofs(blob)
@ -140,6 +142,94 @@ def test_verify_cell_kzg_proof_batch(spec):
proofs_bytes=[proofs[0], proofs[4]], proofs_bytes=[proofs[0], proofs[4]],
) )
# now test with three blobs / commitments
all_blobs = []
all_commitments = []
all_cells = []
all_proofs = []
for _ in range(3):
blob = get_sample_blob(spec)
commitment = spec.blob_to_kzg_commitment(blob)
cells, proofs = spec.compute_cells_and_kzg_proofs(blob)
assert len(cells) == len(proofs)
all_blobs.append(blob)
all_commitments.append(commitment)
all_cells.append(cells)
all_proofs.append(proofs)
# the cells of interest
row_indices = [0, 0, 1, 2, 1]
column_indices = [0, 4, 0, 1, 2]
cells = [all_cells[i][j] for (i, j) in zip(row_indices, column_indices)]
proofs = [all_proofs[i][j] for (i, j) in zip(row_indices, column_indices)]
# do the check
assert spec.verify_cell_kzg_proof_batch(
row_commitments_bytes=all_commitments,
row_indices=row_indices,
column_indices=column_indices,
cells=cells,
proofs_bytes=proofs,
)
@with_eip7594_and_later
@spec_test
@single_phase
def test_verify_cell_kzg_proof_batch_invalid(spec):
# test with a single blob / commitment
blob = get_sample_blob(spec)
commitment = spec.blob_to_kzg_commitment(blob)
cells, proofs = spec.compute_cells_and_kzg_proofs(blob)
assert len(cells) == len(proofs)
assert not spec.verify_cell_kzg_proof_batch(
row_commitments_bytes=[commitment],
row_indices=[0, 0],
column_indices=[0, 4],
cells=[cells[0], cells[5]], # Note: this is where it should go wrong
proofs_bytes=[proofs[0], proofs[4]],
)
# now test with three blobs / commitments
all_blobs = []
all_commitments = []
all_cells = []
all_proofs = []
for _ in range(3):
blob = get_sample_blob(spec)
commitment = spec.blob_to_kzg_commitment(blob)
cells, proofs = spec.compute_cells_and_kzg_proofs(blob)
assert len(cells) == len(proofs)
all_blobs.append(blob)
all_commitments.append(commitment)
all_cells.append(cells)
all_proofs.append(proofs)
# the cells of interest
row_indices = [0, 0, 1, 2, 1]
column_indices = [0, 4, 0, 1, 2]
cells = [all_cells[i][j] for (i, j) in zip(row_indices, column_indices)]
proofs = [all_proofs[i][j] for (i, j) in zip(row_indices, column_indices)]
# let's change one of the cells. Then it should not verify
cells[1] = all_cells[1][3]
# do the check
assert not spec.verify_cell_kzg_proof_batch(
row_commitments_bytes=all_commitments,
row_indices=row_indices,
column_indices=column_indices,
cells=cells,
proofs_bytes=proofs,
)
@with_eip7594_and_later @with_eip7594_and_later
@spec_test @spec_test

View File

@ -80,3 +80,29 @@ def test_fork_random_misc_balances(spec, phases, state):
@with_meta_tags(ELECTRA_FORK_TEST_META_TAGS) @with_meta_tags(ELECTRA_FORK_TEST_META_TAGS)
def test_fork_random_large_validator_set(spec, phases, state): def test_fork_random_large_validator_set(spec, phases, state):
yield from run_fork_test(phases[ELECTRA], state) yield from run_fork_test(phases[ELECTRA], state)
@with_phases(phases=[DENEB], other_phases=[ELECTRA])
@spec_test
@with_state
@with_meta_tags(ELECTRA_FORK_TEST_META_TAGS)
def test_fork_pre_activation(spec, phases, state):
post_spec = phases[ELECTRA]
state.validators[0].activation_epoch = spec.FAR_FUTURE_EPOCH
post_state = yield from run_fork_test(post_spec, state)
assert len(post_state.pending_deposits) > 0
@with_phases(phases=[DENEB], other_phases=[ELECTRA])
@spec_test
@with_state
@with_meta_tags(ELECTRA_FORK_TEST_META_TAGS)
def test_fork_has_compounding_withdrawal_credential(spec, phases, state):
post_spec = phases[ELECTRA]
validator = state.validators[0]
state.balances[0] = post_spec.MIN_ACTIVATION_BALANCE + 1
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_deposits) > 0

View File

@ -63,3 +63,5 @@ def run_fork_test(post_spec, pre_state):
assert post_state.fork.epoch == post_spec.get_current_epoch(post_state) assert post_state.fork.epoch == post_spec.get_current_epoch(post_state)
yield 'post', post_state yield 'post', post_state
return post_state