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)
- [`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)
- [`polynomial_eval_to_coeff`](#polynomial_eval_to_coeff)
- [`add_polynomialcoeff`](#add_polynomialcoeff)
@ -34,7 +35,9 @@
- [KZG multiproofs](#kzg-multiproofs)
- [`compute_kzg_proof_multi_impl`](#compute_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)
- [`coset_shift_for_cell`](#coset_shift_for_cell)
- [`coset_for_cell`](#coset_for_cell)
- [Cells](#cells-1)
- [Cell computation](#cell-computation)
@ -193,17 +196,17 @@ def coset_fft_field(vals: Sequence[BLSFieldElement],
roots_of_unity: Sequence[BLSFieldElement],
inv: bool=False) -> Sequence[BLSFieldElement]:
"""
Computes an FFT/IFFT over a coset of the roots of unity.
This is useful for when one wants to divide by a polynomial which
Computes an FFT/IFFT over a coset of the roots of unity.
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()
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]
"""
"""
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
for i in range(len(vals)):
vals[i] = BLSFieldElement((int(vals[i]) * shift) % BLS_MODULUS)
@ -222,6 +225,52 @@ def coset_fft_field(vals: Sequence[BLSFieldElement],
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
#### `polynomial_eval_to_coeff`
@ -362,12 +411,12 @@ def compute_kzg_proof_multi_impl(
"""
Compute a KZG multi-evaluation proof for a set of `k` points.
This is done by committing to the following quotient polynomial:
This is done by committing to the following quotient polynomial:
Q(X) = f(X) - I(X) / Z(X)
Where:
- I(X) is the degree `k-1` polynomial that agrees with f(x) at all `k` points
- 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)
"""
@ -401,7 +450,7 @@ def verify_kzg_proof_multi_impl(commitment: KZGCommitment,
Q(X) is the quotient polynomial computed by the prover
I(X) is the degree k-1 polynomial that evaluates to `ys` at all `zs`` points
Z(X) is the polynomial that evaluates to zero on all `k` points
The verifier receives the commitments to Q(X) and f(X), so they check the equation
holds by using the following pairing equation:
e([Q(X)]_1, [Z(X)]_2) == e([f(X)]_1 - [I(X)]_1, [1]_2)
@ -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
#### `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`
```python
def coset_for_cell(cell_index: CellIndex) -> Coset:
"""
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
roots_of_unity_brp = bit_reversal_permutation(
@ -457,7 +624,7 @@ def compute_cells_and_kzg_proofs(blob: Blob) -> Tuple[
Public method.
"""
assert len(blob) == BYTES_PER_BLOB
polynomial = blob_to_polynomial(blob)
polynomial_coeff = polynomial_eval_to_coeff(polynomial)
@ -491,7 +658,7 @@ def verify_cell_kzg_proof(commitment_bytes: Bytes48,
assert cell_index < CELLS_PER_EXT_BLOB
assert len(cell) == BYTES_PER_CELL
assert len(proof_bytes) == BYTES_PER_PROOF
coset = coset_for_cell(cell_index)
return verify_kzg_proof_multi_impl(
@ -511,18 +678,15 @@ def verify_cell_kzg_proof_batch(row_commitments_bytes: Sequence[Bytes48],
proofs_bytes: Sequence[Bytes48]) -> bool:
"""
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
individually; an efficient algorithm can be found here:
This function implements the universal verification equation that has been introduced here:
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.
"""
assert len(cells) == len(proofs_bytes) == len(row_indices) == len(column_indices)
for commitment_bytes in row_commitments_bytes:
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:
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
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]
proofs = [bytes_to_kzg_proof(proof_bytes) for proof_bytes in proofs_bytes]
return all(
verify_kzg_proof_multi_impl(commitment, coset_for_cell(column_index), coset_evals, proof)
for commitment, column_index, coset_evals, proof in zip(commitments, column_indices, cosets_evals, proofs)
)
# Do the actual verification
return verify_cell_kzg_proof_batch_impl(row_commitments, row_indices, column_indices, cosets_evals, proofs)
```
## Reconstruction
@ -558,7 +717,7 @@ def construct_vanishing_polynomial(missing_cell_indices: Sequence[CellIndex]) ->
"""
Given the cells indices that are missing from the data, compute the polynomial that vanishes at every point that
corresponds to a missing field element.
This method assumes that all of the cells cannot be missing. In this case the vanishing polynomial
could be computed as Z(x) = x^n - 1, where `n` is FIELD_ELEMENTS_PER_EXT_BLOB.
@ -617,7 +776,7 @@ def recover_data(cell_indices: Sequence[CellIndex],
extended_evaluation_times_zero = [BLSFieldElement(int(a) * int(b) % BLS_MODULUS)
for a, b in zip(zero_poly_eval, extended_evaluation)]
# Convert (E*Z)(x) to monomial form
# Convert (E*Z)(x) to monomial form
extended_evaluation_times_zero_coeffs = fft_field(extended_evaluation_times_zero, roots_of_unity_extended, inv=True)
# Convert (E*Z)(x) to evaluation form over a coset of the FFT domain
@ -684,7 +843,7 @@ def recover_cells_and_kzg_proofs(cell_indices: Sequence[CellIndex],
recovered_cells = [
coset_evals_to_cell(reconstructed_data[i * FIELD_ELEMENTS_PER_CELL:(i + 1) * FIELD_ELEMENTS_PER_CELL])
for i in range(CELLS_PER_EXT_BLOB)]
polynomial_eval = reconstructed_data[:FIELD_ELEMENTS_PER_BLOB]
polynomial_coeff = polynomial_eval_to_coeff(polynomial_eval)
recovered_proofs = [None] * CELLS_PER_EXT_BLOB

View File

@ -44,16 +44,16 @@
- [`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)
- [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)
- [Updated `is_fully_withdrawable_validator`](#updated-is_fully_withdrawable_validator)
- [Updated `is_partially_withdrawable_validator`](#updated-is_partially_withdrawable_validator)
- [Modified `is_fully_withdrawable_validator`](#modified-is_fully_withdrawable_validator)
- [Modified `is_partially_withdrawable_validator`](#modified-is_partially_withdrawable_validator)
- [Misc](#misc-1)
- [`get_committee_indices`](#get_committee_indices)
- [`get_validator_max_effective_balance`](#get_validator_max_effective_balance)
- [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)
@ -61,28 +61,27 @@
- [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)
- [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)
- [Updated `slash_validator`](#updated-slash_validator)
- [Modified `slash_validator`](#modified-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)
- [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)
- [Updated `process_effective_balance_updates`](#updated-process_effective_balance_updates)
- [Modified `process_effective_balance_updates`](#modified-process_effective_balance_updates)
- [Block processing](#block-processing)
- [Withdrawals](#withdrawals)
- [Updated `get_expected_withdrawals`](#updated-get_expected_withdrawals)
- [Updated `process_withdrawals`](#updated-process_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)
@ -90,10 +89,10 @@
- [Attestations](#attestations)
- [Modified `process_attestation`](#modified-process_attestation)
- [Deposits](#deposits)
- [Updated `apply_deposit`](#updated--apply_deposit)
- [Modified `apply_deposit`](#modified-apply_deposit)
- [New `is_valid_deposit_signature`](#new-is_valid_deposit_signature)
- [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)
- [New `process_withdrawal_request`](#new-process_withdrawal_request)
- [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-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
@ -440,9 +439,9 @@ class BeaconState(Container):
### 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
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
```
#### 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
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)
```
#### 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
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
def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> bool:
@ -536,14 +541,14 @@ def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) ->
### Misc
#### `get_committee_indices`
#### New `get_committee_indices`
```python
def get_committee_indices(committee_bits: Bitvector) -> Sequence[CommitteeIndex]:
return [CommitteeIndex(index) for index, bit in enumerate(committee_bits) if bit]
```
#### `get_validator_max_effective_balance`
#### New `get_validator_max_effective_balance`
```python
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`
*Note*: The function `get_attesting_indices` is modified to support EIP7549.
```python
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
```
#### 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 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
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
#### 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
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`
```python
def queue_entire_balance_and_reset_validator(state: BeaconState, index: ValidatorIndex) -> None:
balance = state.balances[index]
@ -797,7 +786,9 @@ def compute_consolidation_epoch_and_update_churn(state: BeaconState, consolidati
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
def slash_validator(state: BeaconState,
@ -831,7 +822,10 @@ def slash_validator(state: BeaconState,
### 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
def process_epoch(state: BeaconState) -> None:
process_justification_and_finalization(state)
@ -850,9 +844,9 @@ def process_epoch(state: BeaconState) -> None:
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.
```python
@ -878,11 +872,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:
@ -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)
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`
@ -959,19 +941,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
# 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)
# Consume churn and apply deposit
processed_amount += churn_consumption
is_deposit_applied = apply_pending_deposit(state, deposit)
# 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
@ -1011,9 +1004,9 @@ def process_pending_consolidations(state: BeaconState) -> None:
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
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)
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
@ -1050,7 +1044,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
#### Withdrawals
##### Updated `get_expected_withdrawals`
##### Modified `get_expected_withdrawals`
*Note*: The function `get_expected_withdrawals` is modified to support EIP7251.
```python
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
```
##### Updated `process_withdrawals`
##### Modified `process_withdrawals`
*Note*: The function `process_withdrawals` is modified to support EIP7251.
```python
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
@ -1272,9 +1270,9 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
##### 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
def apply_deposit(state: BeaconState,
@ -1297,7 +1295,7 @@ def apply_deposit(state: BeaconState,
))
else:
# Increase balance by deposit amount
# [Modified in Electra:EIP-7251]
# [Modified in Electra:EIP7251]
state.pending_deposits.append(PendingDeposit(
pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials,
@ -1325,7 +1323,10 @@ def is_valid_deposit_signature(pubkey: BLSPubkey,
```
##### 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
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`
*Note*: This function is new in Electra following EIP-7002 and EIP-7251.
```python
def process_withdrawal_request(
state: BeaconState,
@ -1422,8 +1421,6 @@ def process_withdrawal_request(
###### New `process_deposit_request`
*Note*: This function is new in Electra:EIP6110.
```python
def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) -> None:
# Set deposit request start index

View File

@ -126,6 +126,8 @@ def test_verify_cell_kzg_proof(spec):
@spec_test
@single_phase
def test_verify_cell_kzg_proof_batch(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)
@ -140,6 +142,94 @@ def test_verify_cell_kzg_proof_batch(spec):
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
@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)
def test_fork_random_large_validator_set(spec, phases, 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)
yield 'post', post_state
return post_state