Merge remote-tracking branch 'upstream/deposit-queue' into deposit-queue
This commit is contained in:
commit
093590ee11
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue