Skip to content

Commit 268166b

Browse files
committed
Merge branch 'dev' into eip-7495
2 parents d281ded + a42d670 commit 268166b

File tree

18 files changed

+841
-145
lines changed

18 files changed

+841
-145
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Features are researched and developed in parallel, and then consolidated into se
2626
### In-development Specifications
2727
| Code Name or Topic | Specs | Notes |
2828
| - | - | - |
29-
| Electra | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/electra/beacon-chain.md)</li><li>[EIP-6110 fork](specs/electra/fork.md)</li></ul><li>Additions</li><ul><li>[Honest validator guide changes](specs/electra/validator.md)</li></ul></ul> |
29+
| Electra | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/electra/beacon-chain.md)</li><li>[EIP-6110 fork](specs/electra/fork.md)</li></ul><li>Additions</li><ul><li>[Light client sync protocol changes](specs/electra/light-client/sync-protocol.md) ([fork](specs/electra/light-client/fork.md), [full node](specs/electra/light-client/full-node.md), [networking](specs/electra/light-client/p2p-interface.md))</li></ul><ul><li>[Honest validator guide changes](specs/electra/validator.md)</li></ul></ul> |
3030
| Sharding (outdated) | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/_features/sharding/beacon-chain.md)</li></ul><li>Additions</li><ul><li>[P2P networking](specs/_features/sharding/p2p-interface.md)</li></ul></ul> |
3131
| Custody Game (outdated) | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/_features/custody_game/beacon-chain.md)</li></ul><li>Additions</li><ul><li>[Honest validator guide changes](specs/_features/custody_game/validator.md)</li></ul></ul> | Dependent on sharding |
3232
| Data Availability Sampling (outdated) | <ul><li>Core</li><ul><li>[Core types and functions](specs/_features/das/das-core.md)</li><li>[Fork choice changes](specs/_features/das/fork-choice.md)</li></ul><li>Additions</li><ul><li>[P2P Networking](specs/_features/das/p2p-interface.md)</li><li>[Sampling process](specs/_features/das/sampling.md)</li></ul></ul> | <ul><li> Dependent on sharding</li><li>[Technical explainer](https://hackmd.io/@HWeNw8hNRimMm2m2GH56Cw/B1YJPGkpD)</li></ul> |

pysetup/spec_builders/electra.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,10 @@ def imports(cls, preset_name: str):
1212
from eth2spec.deneb import {preset_name} as deneb
1313
'''
1414

15-
## TODO: deal with changed gindices
16-
1715
@classmethod
1816
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
1917
return {
20-
'FINALIZED_ROOT_GINDEX': 'GeneralizedIndex(169)',
21-
'CURRENT_SYNC_COMMITTEE_GINDEX': 'GeneralizedIndex(86)',
22-
'NEXT_SYNC_COMMITTEE_GINDEX': 'GeneralizedIndex(87)',
18+
'FINALIZED_ROOT_GINDEX_ELECTRA': 'GeneralizedIndex(169)',
19+
'CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA': 'GeneralizedIndex(86)',
20+
'NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA': 'GeneralizedIndex(87)',
2321
}

specs/_features/eip7594/polynomial-commitments-sampling.md

Lines changed: 41 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@
3939
- [`coset_for_cell`](#coset_for_cell)
4040
- [Cells](#cells-1)
4141
- [Cell computation](#cell-computation)
42+
- [`compute_cells_and_kzg_proofs_polynomialcoeff`](#compute_cells_and_kzg_proofs_polynomialcoeff)
4243
- [`compute_cells_and_kzg_proofs`](#compute_cells_and_kzg_proofs)
4344
- [Cell verification](#cell-verification)
4445
- [`verify_cell_kzg_proof_batch`](#verify_cell_kzg_proof_batch)
4546
- [Reconstruction](#reconstruction)
4647
- [`construct_vanishing_polynomial`](#construct_vanishing_polynomial)
47-
- [`recover_data`](#recover_data)
48+
- [`recover_polynomialcoeff`](#recover_polynomialcoeff)
4849
- [`recover_cells_and_kzg_proofs`](#recover_cells_and_kzg_proofs)
4950

5051
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -555,6 +556,24 @@ def coset_for_cell(cell_index: CellIndex) -> Coset:
555556

556557
### Cell computation
557558

559+
#### `compute_cells_and_kzg_proofs_polynomialcoeff`
560+
561+
```python
562+
def compute_cells_and_kzg_proofs_polynomialcoeff(polynomial_coeff: PolynomialCoeff) -> Tuple[
563+
Vector[Cell, CELLS_PER_EXT_BLOB],
564+
Vector[KZGProof, CELLS_PER_EXT_BLOB]]:
565+
"""
566+
Helper function which computes cells/proofs for a polynomial in coefficient form.
567+
"""
568+
cells, proofs = [], []
569+
for i in range(CELLS_PER_EXT_BLOB):
570+
coset = coset_for_cell(CellIndex(i))
571+
proof, ys = compute_kzg_proof_multi_impl(polynomial_coeff, coset)
572+
cells.append(coset_evals_to_cell(ys))
573+
proofs.append(proof)
574+
return cells, proofs
575+
```
576+
558577
#### `compute_cells_and_kzg_proofs`
559578

560579
```python
@@ -572,17 +591,7 @@ def compute_cells_and_kzg_proofs(blob: Blob) -> Tuple[
572591

573592
polynomial = blob_to_polynomial(blob)
574593
polynomial_coeff = polynomial_eval_to_coeff(polynomial)
575-
576-
cells = []
577-
proofs = []
578-
579-
for i in range(CELLS_PER_EXT_BLOB):
580-
coset = coset_for_cell(CellIndex(i))
581-
proof, ys = compute_kzg_proof_multi_impl(polynomial_coeff, coset)
582-
cells.append(coset_evals_to_cell(ys))
583-
proofs.append(proof)
584-
585-
return cells, proofs
594+
return compute_cells_and_kzg_proofs_polynomialcoeff(polynomial_coeff)
586595
```
587596

588597
### Cell verification
@@ -668,29 +677,27 @@ def construct_vanishing_polynomial(missing_cell_indices: Sequence[CellIndex]) ->
668677
return zero_poly_coeff
669678
```
670679

671-
### `recover_data`
680+
### `recover_polynomialcoeff`
672681

673682
```python
674-
def recover_data(cell_indices: Sequence[CellIndex],
675-
cells: Sequence[Cell],
676-
) -> Sequence[BLSFieldElement]:
683+
def recover_polynomialcoeff(cell_indices: Sequence[CellIndex],
684+
cells: Sequence[Cell]) -> Sequence[BLSFieldElement]:
677685
"""
678-
Recover the missing evaluations for the extended blob, given at least half of the evaluations.
686+
Recover the polynomial in coefficient form that when evaluated at the roots of unity will give the extended blob.
679687
"""
680-
681-
# Get the extended domain. This will be referred to as the FFT domain.
688+
# Get the extended domain. This will be referred to as the FFT domain
682689
roots_of_unity_extended = compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB)
683690

684-
# Flatten the cells into evaluations.
685-
# If a cell is missing, then its evaluation is zero.
691+
# Flatten the cells into evaluations
692+
# If a cell is missing, then its evaluation is zero
686693
extended_evaluation_rbo = [0] * FIELD_ELEMENTS_PER_EXT_BLOB
687694
for cell_index, cell in zip(cell_indices, cells):
688695
start = cell_index * FIELD_ELEMENTS_PER_CELL
689696
end = (cell_index + 1) * FIELD_ELEMENTS_PER_CELL
690697
extended_evaluation_rbo[start:end] = cell
691698
extended_evaluation = bit_reversal_permutation(extended_evaluation_rbo)
692699

693-
# Compute Z(x) in monomial form
700+
# Compute Z(x) in coefficient form
694701
# Z(x) is the polynomial which vanishes on all of the evaluations which are missing
695702
missing_cell_indices = [CellIndex(cell_index) for cell_index in range(CELLS_PER_EXT_BLOB)
696703
if cell_index not in cell_indices]
@@ -703,7 +710,7 @@ def recover_data(cell_indices: Sequence[CellIndex],
703710
extended_evaluation_times_zero = [BLSFieldElement(int(a) * int(b) % BLS_MODULUS)
704711
for a, b in zip(zero_poly_eval, extended_evaluation)]
705712

706-
# Convert (E*Z)(x) to monomial form
713+
# Convert (E*Z)(x) to coefficient form
707714
extended_evaluation_times_zero_coeffs = fft_field(extended_evaluation_times_zero, roots_of_unity_extended, inv=True)
708715

709716
# Convert (E*Z)(x) to evaluation form over a coset of the FFT domain
@@ -713,18 +720,12 @@ def recover_data(cell_indices: Sequence[CellIndex],
713720
zero_poly_over_coset = coset_fft_field(zero_poly_coeff, roots_of_unity_extended)
714721

715722
# Compute Q_3(x) = (E*Z)(x) / Z(x) in evaluation form over a coset of the FFT domain
716-
reconstructed_poly_over_coset = [
717-
div(a, b)
718-
for a, b in zip(extended_evaluations_over_coset, zero_poly_over_coset)
719-
]
723+
reconstructed_poly_over_coset = [div(a, b) for a, b in zip(extended_evaluations_over_coset, zero_poly_over_coset)]
720724

721-
# Convert Q_3(x) to monomial form
725+
# Convert Q_3(x) to coefficient form
722726
reconstructed_poly_coeff = coset_fft_field(reconstructed_poly_over_coset, roots_of_unity_extended, inv=True)
723727

724-
# Convert Q_3(x) to evaluation form over the FFT domain and bit reverse the result
725-
reconstructed_data = bit_reversal_permutation(fft_field(reconstructed_poly_coeff, roots_of_unity_extended))
726-
727-
return reconstructed_data
728+
return reconstructed_poly_coeff[:FIELD_ELEMENTS_PER_BLOB]
728729
```
729730

730731
### `recover_cells_and_kzg_proofs`
@@ -735,7 +736,7 @@ def recover_cells_and_kzg_proofs(cell_indices: Sequence[CellIndex],
735736
Vector[Cell, CELLS_PER_EXT_BLOB],
736737
Vector[KZGProof, CELLS_PER_EXT_BLOB]]:
737738
"""
738-
Given at least 50% of cells/proofs for a blob, recover all the cells/proofs.
739+
Given at least 50% of cells for a blob, recover all the cells/proofs.
739740
This algorithm uses FFTs to recover cells faster than using Lagrange
740741
implementation, as can be seen here:
741742
https://ethresear.ch/t/reed-solomon-erasure-code-recovery-in-n-log-2-n-time-with-ffts/3039
@@ -745,6 +746,7 @@ def recover_cells_and_kzg_proofs(cell_indices: Sequence[CellIndex],
745746
746747
Public method.
747748
"""
749+
# Check we have the same number of cells and indices
748750
assert len(cell_indices) == len(cells)
749751
# Check we have enough cells to be able to perform the reconstruction
750752
assert CELLS_PER_EXT_BLOB / 2 <= len(cell_indices) <= CELLS_PER_EXT_BLOB
@@ -757,29 +759,12 @@ def recover_cells_and_kzg_proofs(cell_indices: Sequence[CellIndex],
757759
for cell in cells:
758760
assert len(cell) == BYTES_PER_CELL
759761

760-
# Convert cells to coset evals
762+
# Convert cells to coset evaluations
761763
cosets_evals = [cell_to_coset_evals(cell) for cell in cells]
762764

763-
reconstructed_data = recover_data(cell_indices, cosets_evals)
764-
765-
for cell_index, coset_evals in zip(cell_indices, cosets_evals):
766-
start = cell_index * FIELD_ELEMENTS_PER_CELL
767-
end = (cell_index + 1) * FIELD_ELEMENTS_PER_CELL
768-
assert reconstructed_data[start:end] == coset_evals
769-
770-
recovered_cells = [
771-
coset_evals_to_cell(reconstructed_data[i * FIELD_ELEMENTS_PER_CELL:(i + 1) * FIELD_ELEMENTS_PER_CELL])
772-
for i in range(CELLS_PER_EXT_BLOB)]
773-
774-
polynomial_eval = reconstructed_data[:FIELD_ELEMENTS_PER_BLOB]
775-
polynomial_coeff = polynomial_eval_to_coeff(polynomial_eval)
776-
recovered_proofs = [None] * CELLS_PER_EXT_BLOB
777-
778-
for i in range(CELLS_PER_EXT_BLOB):
779-
coset = coset_for_cell(CellIndex(i))
780-
proof, ys = compute_kzg_proof_multi_impl(polynomial_coeff, coset)
781-
assert coset_evals_to_cell(ys) == recovered_cells[i]
782-
recovered_proofs[i] = proof
783-
784-
return recovered_cells, recovered_proofs
765+
# Given the coset evaluations, recover the polynomial in coefficient form
766+
polynomial_coeff = recover_polynomialcoeff(cell_indices, cosets_evals)
767+
768+
# Recompute all cells/proofs
769+
return compute_cells_and_kzg_proofs_polynomialcoeff(polynomial_coeff)
785770
```

specs/altair/light-client/full-node.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def create_light_client_bootstrap(state: BeaconState,
7676
header=block_to_light_client_header(block),
7777
current_sync_committee=state.current_sync_committee,
7878
current_sync_committee_branch=CurrentSyncCommitteeBranch(
79-
compute_merkle_proof(state, CURRENT_SYNC_COMMITTEE_GINDEX)),
79+
compute_merkle_proof(state, current_sync_committee_gindex_at_slot(state.slot))),
8080
)
8181
```
8282

@@ -124,7 +124,7 @@ def create_light_client_update(state: BeaconState,
124124
if update_attested_period == update_signature_period:
125125
update.next_sync_committee = attested_state.next_sync_committee
126126
update.next_sync_committee_branch = NextSyncCommitteeBranch(
127-
compute_merkle_proof(attested_state, NEXT_SYNC_COMMITTEE_GINDEX))
127+
compute_merkle_proof(attested_state, next_sync_committee_gindex_at_slot(attested_state.slot)))
128128

129129
# Indicate finality whenever possible
130130
if finalized_block is not None:
@@ -134,7 +134,7 @@ def create_light_client_update(state: BeaconState,
134134
else:
135135
assert attested_state.finalized_checkpoint.root == Bytes32()
136136
update.finality_branch = FinalityBranch(
137-
compute_merkle_proof(attested_state, FINALIZED_ROOT_GINDEX))
137+
compute_merkle_proof(attested_state, finalized_root_gindex_at_slot(attested_state.slot)))
138138

139139
update.sync_aggregate = block.message.body.sync_aggregate
140140
update.signature_slot = block.message.slot

specs/altair/light-client/sync-protocol.md

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@
2121
- [`LightClientOptimisticUpdate`](#lightclientoptimisticupdate)
2222
- [`LightClientStore`](#lightclientstore)
2323
- [Helper functions](#helper-functions)
24+
- [`finalized_root_gindex_at_slot`](#finalized_root_gindex_at_slot)
25+
- [`current_sync_committee_gindex_at_slot`](#current_sync_committee_gindex_at_slot)
26+
- [`next_sync_committee_gindex_at_slot`](#next_sync_committee_gindex_at_slot)
2427
- [`is_valid_light_client_header`](#is_valid_light_client_header)
2528
- [`is_sync_committee_update`](#is_sync_committee_update)
2629
- [`is_finality_update`](#is_finality_update)
2730
- [`is_better_update`](#is_better_update)
2831
- [`is_next_sync_committee_known`](#is_next_sync_committee_known)
2932
- [`get_safety_threshold`](#get_safety_threshold)
3033
- [`get_subtree_index`](#get_subtree_index)
34+
- [`is_valid_normalized_merkle_branch`](#is_valid_normalized_merkle_branch)
3135
- [`compute_sync_committee_period_at_slot`](#compute_sync_committee_period_at_slot)
3236
- [Light client initialization](#light-client-initialization)
3337
- [`initialize_light_client_store`](#initialize_light_client_store)
@@ -171,6 +175,30 @@ class LightClientStore(object):
171175

172176
## Helper functions
173177

178+
### `finalized_root_gindex_at_slot`
179+
180+
```python
181+
def finalized_root_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
182+
# pylint: disable=unused-argument
183+
return FINALIZED_ROOT_GINDEX
184+
```
185+
186+
### `current_sync_committee_gindex_at_slot`
187+
188+
```python
189+
def current_sync_committee_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
190+
# pylint: disable=unused-argument
191+
return CURRENT_SYNC_COMMITTEE_GINDEX
192+
```
193+
194+
### `next_sync_committee_gindex_at_slot`
195+
196+
```python
197+
def next_sync_committee_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
198+
# pylint: disable=unused-argument
199+
return NEXT_SYNC_COMMITTEE_GINDEX
200+
```
201+
174202
### `is_valid_light_client_header`
175203

176204
```python
@@ -273,6 +301,22 @@ def get_subtree_index(generalized_index: GeneralizedIndex) -> uint64:
273301
return uint64(generalized_index % 2**(floorlog2(generalized_index)))
274302
```
275303

304+
### `is_valid_normalized_merkle_branch`
305+
306+
```python
307+
def is_valid_normalized_merkle_branch(leaf: Bytes32,
308+
branch: Sequence[Bytes32],
309+
gindex: GeneralizedIndex,
310+
root: Root) -> bool:
311+
depth = floorlog2(gindex)
312+
index = get_subtree_index(gindex)
313+
num_extra = len(branch) - depth
314+
for i in range(num_extra):
315+
if branch[i] != Bytes32():
316+
return False
317+
return is_valid_merkle_branch(leaf, branch[num_extra:], depth, index, root)
318+
```
319+
276320
### `compute_sync_committee_period_at_slot`
277321

278322
```python
@@ -292,11 +336,10 @@ def initialize_light_client_store(trusted_block_root: Root,
292336
assert is_valid_light_client_header(bootstrap.header)
293337
assert hash_tree_root(bootstrap.header.beacon) == trusted_block_root
294338

295-
assert is_valid_merkle_branch(
339+
assert is_valid_normalized_merkle_branch(
296340
leaf=hash_tree_root(bootstrap.current_sync_committee),
297341
branch=bootstrap.current_sync_committee_branch,
298-
depth=floorlog2(CURRENT_SYNC_COMMITTEE_GINDEX),
299-
index=get_subtree_index(CURRENT_SYNC_COMMITTEE_GINDEX),
342+
gindex=current_sync_committee_gindex_at_slot(bootstrap.header.beacon.slot),
300343
root=bootstrap.header.beacon.state_root,
301344
)
302345

@@ -364,11 +407,10 @@ def validate_light_client_update(store: LightClientStore,
364407
else:
365408
assert is_valid_light_client_header(update.finalized_header)
366409
finalized_root = hash_tree_root(update.finalized_header.beacon)
367-
assert is_valid_merkle_branch(
410+
assert is_valid_normalized_merkle_branch(
368411
leaf=finalized_root,
369412
branch=update.finality_branch,
370-
depth=floorlog2(FINALIZED_ROOT_GINDEX),
371-
index=get_subtree_index(FINALIZED_ROOT_GINDEX),
413+
gindex=finalized_root_gindex_at_slot(update.attested_header.beacon.slot),
372414
root=update.attested_header.beacon.state_root,
373415
)
374416

@@ -379,11 +421,10 @@ def validate_light_client_update(store: LightClientStore,
379421
else:
380422
if update_attested_period == store_period and is_next_sync_committee_known(store):
381423
assert update.next_sync_committee == store.next_sync_committee
382-
assert is_valid_merkle_branch(
424+
assert is_valid_normalized_merkle_branch(
383425
leaf=hash_tree_root(update.next_sync_committee),
384426
branch=update.next_sync_committee_branch,
385-
depth=floorlog2(NEXT_SYNC_COMMITTEE_GINDEX),
386-
index=get_subtree_index(NEXT_SYNC_COMMITTEE_GINDEX),
427+
gindex=next_sync_committee_gindex_at_slot(update.attested_header.beacon.slot),
387428
root=update.attested_header.beacon.state_root,
388429
)
389430

specs/capella/light-client/fork.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
88

99
- [Introduction](#introduction)
10-
- [Upgrading light client data](#upgrading-light-client-data)
11-
- [Upgrading the store](#upgrading-the-store)
10+
- [Upgrading light client data](#upgrading-light-client-data)
11+
- [Upgrading the store](#upgrading-the-store)
1212

1313
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
1414
<!-- /TOC -->
@@ -17,7 +17,7 @@
1717

1818
This document describes how to upgrade existing light client objects based on the [Altair specification](../../altair/light-client/sync-protocol.md) to Capella. This is necessary when processing pre-Capella data with a post-Capella `LightClientStore`. Note that the data being exchanged over the network protocols uses the original format.
1919

20-
### Upgrading light client data
20+
## Upgrading light client data
2121

2222
A Capella `LightClientStore` can still process earlier light client data. In order to do so, that pre-Capella data needs to be locally upgraded to Capella before processing.
2323

@@ -70,7 +70,7 @@ def upgrade_lc_optimistic_update_to_capella(pre: bellatrix.LightClientOptimistic
7070
)
7171
```
7272

73-
### Upgrading the store
73+
## Upgrading the store
7474

7575
Existing `LightClientStore` objects based on Altair MUST be upgraded to Capella before Capella based light client data can be processed. The `LightClientStore` upgrade MAY be performed before `CAPELLA_FORK_EPOCH`.
7676

0 commit comments

Comments
 (0)