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)
@ -193,17 +196,17 @@ def coset_fft_field(vals: Sequence[BLSFieldElement],
roots_of_unity: Sequence[BLSFieldElement], roots_of_unity: Sequence[BLSFieldElement],
inv: bool=False) -> Sequence[BLSFieldElement]: inv: bool=False) -> Sequence[BLSFieldElement]:
""" """
Computes an FFT/IFFT over a coset of the roots of unity. 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 This is useful for when one wants to divide by a polynomial which
vanishes on one or more elements in the domain. vanishes on one or more elements in the domain.
""" """
vals = vals.copy() vals = vals.copy()
def shift_vals(vals: Sequence[BLSFieldElement], factor: BLSFieldElement) -> Sequence[BLSFieldElement]: def shift_vals(vals: Sequence[BLSFieldElement], factor: BLSFieldElement) -> Sequence[BLSFieldElement]:
""" """
Multiply each entry in `vals` by succeeding powers of `factor` Multiply each entry in `vals` by succeeding powers of `factor`
i.e., [vals[0] * factor^0, vals[1] * factor^1, ..., vals[n] * factor^n] i.e., [vals[0] * factor^0, vals[1] * factor^1, ..., vals[n] * factor^n]
""" """
shift = 1 shift = 1
for i in range(len(vals)): for i in range(len(vals)):
vals[i] = BLSFieldElement((int(vals[i]) * shift) % BLS_MODULUS) 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) 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`
@ -362,12 +411,12 @@ def compute_kzg_proof_multi_impl(
""" """
Compute a KZG multi-evaluation proof for a set of `k` points. 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) Q(X) = f(X) - I(X) / Z(X)
Where: Where:
- I(X) is the degree `k-1` polynomial that agrees with f(x) at all `k` points - 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 - 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), 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)
""" """
@ -401,7 +450,7 @@ def verify_kzg_proof_multi_impl(commitment: KZGCommitment,
Q(X) is the quotient polynomial computed by the prover 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 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 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 The verifier receives the commitments to Q(X) and f(X), so they check the equation
holds by using the following pairing equation: holds by using the following pairing equation:
e([Q(X)]_1, [Z(X)]_2) == e([f(X)]_1 - [I(X)]_1, [1]_2) 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 ### 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(
@ -457,7 +624,7 @@ def compute_cells_and_kzg_proofs(blob: Blob) -> Tuple[
Public method. Public method.
""" """
assert len(blob) == BYTES_PER_BLOB assert len(blob) == BYTES_PER_BLOB
polynomial = blob_to_polynomial(blob) polynomial = blob_to_polynomial(blob)
polynomial_coeff = polynomial_eval_to_coeff(polynomial) 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 cell_index < CELLS_PER_EXT_BLOB
assert len(cell) == BYTES_PER_CELL assert len(cell) == BYTES_PER_CELL
assert len(proof_bytes) == BYTES_PER_PROOF assert len(proof_bytes) == BYTES_PER_PROOF
coset = coset_for_cell(cell_index) coset = coset_for_cell(cell_index)
return verify_kzg_proof_multi_impl( 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: 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
@ -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 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. corresponds to a missing field element.
This method assumes that all of the cells cannot be missing. In this case the vanishing polynomial 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. 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) extended_evaluation_times_zero = [BLSFieldElement(int(a) * int(b) % BLS_MODULUS)
for a, b in zip(zero_poly_eval, extended_evaluation)] 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) 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 # 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 = [ recovered_cells = [
coset_evals_to_cell(reconstructed_data[i * FIELD_ELEMENTS_PER_CELL:(i + 1) * FIELD_ELEMENTS_PER_CELL]) 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)] for i in range(CELLS_PER_EXT_BLOB)]
polynomial_eval = reconstructed_data[:FIELD_ELEMENTS_PER_BLOB] polynomial_eval = reconstructed_data[:FIELD_ELEMENTS_PER_BLOB]
polynomial_coeff = polynomial_eval_to_coeff(polynomial_eval) polynomial_coeff = polynomial_eval_to_coeff(polynomial_eval)
recovered_proofs = [None] * CELLS_PER_EXT_BLOB recovered_proofs = [None] * CELLS_PER_EXT_BLOB

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