SAMPLE -> CELL and cleanups

This commit is contained in:
Dankrad Feist 2023-12-23 14:44:58 +00:00
parent 62d1a8f4c4
commit 246477ace2
No known key found for this signature in database
GPG Key ID: 6815E6A20BEBBABA
1 changed files with 44 additions and 59 deletions

View File

@ -43,7 +43,7 @@
- [`verify_sample_proof`](#verify_sample_proof)
- [`verify_sample_proof_batch`](#verify_sample_proof_batch)
- [Reconstruction](#reconstruction)
- [`recover_samples_impl`](#recover_samples_impl)
- [`recover_samples`](#recover_samples)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
@ -73,18 +73,18 @@ Public functions MUST accept raw bytes as input and perform the required cryptog
| Name | Value | Description |
| - | - | - |
| `FIELD_ELEMENTS_PER_SAMPLE` | `uint64(64)` | Number of field elements in a sample |
| `BYTES_PER_SAMPLE` | `FIELD_ELEMENTS_PER_SAMPLE * BYTES_PER_FIELD_ELEMENT` | The number of bytes in a sample |
| `SAMPLES_PER_BLOB` | `((2 * FIELD_ELEMENTS_PER_BLOB) // FIELD_ELEMENTS_PER_SAMPLE)` | The number of samples for a blob |
| `FIELD_ELEMENTS_PER_CELL` | `uint64(64)` | Number of field elements in a cell |
| `BYTES_PER_CELL` | `FIELD_ELEMENTS_PER_CELL * BYTES_PER_FIELD_ELEMENT` | The number of bytes in a cell |
| `CELLS_PER_BLOB` | `((2 * FIELD_ELEMENTS_PER_BLOB) // FIELD_ELEMENTS_PER_CELL)` | The number of cells in a blob |
### Crypto
| Name | Value | Description |
| - | - | - |
| `ROOT_OF_UNITY2` | `pow(PRIMITIVE_ROOT_OF_UNITY, (BLS_MODULUS - 1) // int(FIELD_ELEMENTS_PER_BLOB * 2), BLS_MODULUS)` | Root of unity of order FIELD_ELEMENTS_PER_BLOB * 2 over the BLS12-381 field |
| `ROOTS_OF_UNITY2` | `([pow(ROOT_OF_UNITY, i, BLS_MODULUS) for i in range(FIELD_ELEMENTS_PER_BLOB * 2)])` | Roots of unity of order FIELD_ELEMENTS_PER_BLOB * 2 over the BLS12-381 field |
| `ROOT_OF_UNITY_S` | `pow(PRIMITIVE_ROOT_OF_UNITY, (BLS_MODULUS - 1) // int(SAMPLES_PER_BLOB), BLS_MODULUS)` | Root of unity of order SAMPLES_PER_BLOB over the BLS12-381 field |
| `ROOTS_OF_UNITY_S` | `([pow(ROOT_OF_UNITY, i, BLS_MODULUS) for i in range(SAMPLES_PER_BLOB)])` | Roots of unity of order SAMPLES_PER_BLOB over the BLS12-381 field |
| `ROOT_OF_UNITY_EXTENDED` | `pow(PRIMITIVE_ROOT_OF_UNITY, (BLS_MODULUS - 1) // int(FIELD_ELEMENTS_PER_BLOB * 2), BLS_MODULUS)` | Root of unity of order FIELD_ELEMENTS_PER_BLOB * 2 over the BLS12-381 field |
| `ROOTS_OF_UNITY_EXTENDED` | `([pow(ROOT_OF_UNITY_EXTENDED, i, BLS_MODULUS) for i in range(FIELD_ELEMENTS_PER_BLOB * 2)])` | Roots of unity of order FIELD_ELEMENTS_PER_BLOB * 2 over the BLS12-381 field |
| `ROOT_OF_UNITY_REDUCED` | `pow(PRIMITIVE_ROOT_OF_UNITY, (BLS_MODULUS - 1) // int(CELLS_PER_BLOB), BLS_MODULUS)` | Root of unity of order CELLS_PER_BLOB over the BLS12-381 field |
| `ROOTS_OF_UNITY_REDUCED` | `([pow(ROOT_OF_UNITY_REDUCED, i, BLS_MODULUS) for i in range(CELLS_PER_BLOB)])` | Roots of unity of order CELLS_PER_BLOB over the BLS12-381 field |
## Helper functions
@ -106,27 +106,12 @@ def g2_lincomb(points: Sequence[KZGCommitment], scalars: Sequence[BLSFieldElemen
### FFTs
#### `_simple_ft_field`
```python
def _simple_ft_field(vals, roots_of_unity):
assert len(vals) == len(roots_of_unity)
L = len(roots_of_unity)
o = []
for i in range(L):
last = 0
for j in range(L):
last += int(vals[j]) * int(roots_of_unity[(i * j) % L]) % BLS_MODULUS
o.append(last % BLS_MODULUS)
return o
```
#### `_fft_field`
```python
def _fft_field(vals, roots_of_unity):
if len(vals) <= 4:
return _simple_ft_field(vals, roots_of_unity)
if len(vals) == 0:
return vals
L = _fft_field(vals[::2], roots_of_unity[::2])
R = _fft_field(vals[1::2], roots_of_unity[::2])
o = [0 for i in vals]
@ -361,14 +346,14 @@ def verify_kzg_proof_multi_impl(commitment: KZGCommitment,
#### `coset_for_sample`
```python
def coset_for_sample(sample_id: int) -> Vector[BLSFieldElement, FIELD_ELEMENTS_PER_SAMPLE]:
def coset_for_sample(sample_id: int) -> Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]:
"""
Get the subgroup for a given ``sample_id``
Get the coset for a given ``sample_id``
"""
assert sample_id < SAMPLES_PER_BLOB
roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY2)
return Vector[BLSFieldElement, FIELD_ELEMENTS_PER_SAMPLE](
roots_of_unity_brp[FIELD_ELEMENTS_PER_SAMPLE * sample_id:FIELD_ELEMENTS_PER_SAMPLE * (sample_id + 1)]
assert sample_id < CELLS_PER_BLOB
roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY_EXTENDED)
return Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL](
roots_of_unity_brp[FIELD_ELEMENTS_PER_CELL * sample_id:FIELD_ELEMENTS_PER_CELL * (sample_id + 1)]
)
```
@ -380,8 +365,8 @@ def coset_for_sample(sample_id: int) -> Vector[BLSFieldElement, FIELD_ELEMENTS_P
```python
def compute_samples_and_proofs(blob: Blob) -> Tuple[
Vector[Vector[BLSFieldElement, FIELD_ELEMENTS_PER_SAMPLE], SAMPLES_PER_BLOB],
Vector[KZGProof, SAMPLES_PER_BLOB]]:
Vector[Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL], CELLS_PER_BLOB],
Vector[KZGProof, CELLS_PER_BLOB]]:
"""
Compute all the sample proofs for one blob. This is an inefficient O(n^2) algorithm,
for performant implementation the FK20 algorithm that runs in O(n log n) should be
@ -395,7 +380,7 @@ def compute_samples_and_proofs(blob: Blob) -> Tuple[
samples = []
proofs = []
for i in range(SAMPLES_PER_BLOB):
for i in range(CELLS_PER_BLOB):
coset = coset_for_sample(i)
proof, ys = compute_kzg_proof_multi_impl(polynomial_coeff, coset)
samples.append(ys)
@ -407,7 +392,7 @@ def compute_samples_and_proofs(blob: Blob) -> Tuple[
#### `compute_samples`
```python
def compute_samples(blob: Blob) -> Vector[Vector[BLSFieldElement, FIELD_ELEMENTS_PER_SAMPLE], SAMPLES_PER_BLOB]:
def compute_samples(blob: Blob) -> Vector[Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL], CELLS_PER_BLOB]:
"""
Compute the sample data for a blob (without computing the proofs).
@ -416,10 +401,10 @@ def compute_samples(blob: Blob) -> Vector[Vector[BLSFieldElement, FIELD_ELEMENTS
polynomial = blob_to_polynomial(blob)
polynomial_coeff = polynomial_eval_to_coeff(polynomial)
extended_data = fft_field(polynomial_coeff + [0] * FIELD_ELEMENTS_PER_BLOB, ROOTS_OF_UNITY2)
extended_data = fft_field(polynomial_coeff + [0] * FIELD_ELEMENTS_PER_BLOB, ROOTS_OF_UNITY_EXTENDED)
extended_data_rbo = bit_reversal_permutation(extended_data)
return [extended_data_rbo[i * FIELD_ELEMENTS_PER_SAMPLE:(i + 1) * FIELD_ELEMENTS_PER_SAMPLE]
for i in range(SAMPLES_PER_BLOB)]
return [extended_data_rbo[i * FIELD_ELEMENTS_PER_CELL:(i + 1) * FIELD_ELEMENTS_PER_CELL]
for i in range(CELLS_PER_BLOB)]
```
### Sample verification
@ -429,12 +414,12 @@ def compute_samples(blob: Blob) -> Vector[Vector[BLSFieldElement, FIELD_ELEMENTS
```python
def verify_sample_proof(commitment: KZGCommitment,
sample_id: int,
data: Vector[BLSFieldElement, FIELD_ELEMENTS_PER_SAMPLE],
data: Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL],
proof: KZGProof) -> bool:
"""
Check a sample proof
Publiiic method.
Public method.
"""
coset = coset_for_sample(sample_id)
@ -447,7 +432,7 @@ def verify_sample_proof(commitment: KZGCommitment,
def verify_sample_proof_batch(row_commitments: Sequence[KZGCommitment],
row_ids: Sequence[int],
column_ids: Sequence[int],
datas: Sequence[Vector[BLSFieldElement, FIELD_ELEMENTS_PER_SAMPLE]],
datas: Sequence[Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]],
proofs: Sequence[KZGProof]) -> bool:
"""
Check multiple sample proofs. This function implements the naive algorithm of checking every sample
@ -466,12 +451,12 @@ def verify_sample_proof_batch(row_commitments: Sequence[KZGCommitment],
## Reconstruction
### `recover_samples_impl`
### `recover_samples`
```python
def recover_samples_impl(samples: Sequence[Tuple[int, Sequence[BLSFieldElement]]]) -> Polynomial:
def recover_samples(samples: Sequence[Tuple[int, ByteVector[BYTES_PER_CELL]]]) -> Polynomial:
"""
Recovers a polynomial from 2 * FIELD_ELEMENTS_PER_SAMPLE evaluations, half of which can be missing.
Recovers a polynomial from 2 * FIELD_ELEMENTS_PER_CELL evaluations, half of which can be missing.
This algorithm uses FFTs to recover samples faster than using Lagrange implementation. However,
a faster version thanks to Qi Zhou can be found here:
@ -479,34 +464,34 @@ def recover_samples_impl(samples: Sequence[Tuple[int, Sequence[BLSFieldElement]]
Public method.
"""
assert len(samples) >= SAMPLES_PER_BLOB // 2
assert len(samples) >= CELLS_PER_BLOB // 2
sample_ids = [sample_id for sample_id, _ in samples]
missing_sample_ids = [sample_id for sample_id in range(SAMPLES_PER_BLOB) if sample_id not in sample_ids]
short_zero_poly = zero_polynomialcoeff([ROOTS_OF_UNITY_S[reverse_bits(sample_id, SAMPLES_PER_BLOB)] for sample_id in missing_sample_ids])
missing_sample_ids = [sample_id for sample_id in range(CELLS_PER_BLOB) if sample_id not in sample_ids]
short_zero_poly = zero_polynomialcoeff([ROOTS_OF_UNITY_REDUCED[reverse_bits(sample_id, CELLS_PER_BLOB)] for sample_id in missing_sample_ids])
full_zero_poly = []
for i in short_zero_poly:
full_zero_poly.append(i)
full_zero_poly.extend([0] * (FIELD_ELEMENTS_PER_SAMPLE - 1))
full_zero_poly.extend([0] * (FIELD_ELEMENTS_PER_CELL - 1))
full_zero_poly = full_zero_poly + [0] * (2 * FIELD_ELEMENTS_PER_BLOB - len(full_zero_poly))
zero_poly_eval = fft_field(full_zero_poly, ROOTS_OF_UNITY2)
zero_poly_eval = fft_field(full_zero_poly, ROOTS_OF_UNITY_EXTENDED)
zero_poly_eval_brp = bit_reversal_permutation(zero_poly_eval)
for sample_id in missing_sample_ids:
assert zero_poly_eval_brp[sample_id * FIELD_ELEMENTS_PER_SAMPLE:(sample_id + 1) * FIELD_ELEMENTS_PER_SAMPLE] == \
[0] * FIELD_ELEMENTS_PER_SAMPLE
assert zero_poly_eval_brp[sample_id * FIELD_ELEMENTS_PER_CELL:(sample_id + 1) * FIELD_ELEMENTS_PER_CELL] == \
[0] * FIELD_ELEMENTS_PER_CELL
for sample_id in sample_ids:
assert all(a != 0 for a in zero_poly_eval_brp[sample_id * FIELD_ELEMENTS_PER_SAMPLE:(sample_id + 1) * FIELD_ELEMENTS_PER_SAMPLE])
assert all(a != 0 for a in zero_poly_eval_brp[sample_id * FIELD_ELEMENTS_PER_CELL:(sample_id + 1) * FIELD_ELEMENTS_PER_CELL])
extended_evaluation_rbo = [0] * FIELD_ELEMENTS_PER_BLOB * 2
for sample_id, sample_data in samples:
extended_evaluation_rbo[sample_id * FIELD_ELEMENTS_PER_SAMPLE:(sample_id + 1) * FIELD_ELEMENTS_PER_SAMPLE] = \
extended_evaluation_rbo[sample_id * FIELD_ELEMENTS_PER_CELL:(sample_id + 1) * FIELD_ELEMENTS_PER_CELL] = \
sample_data
extended_evaluation = bit_reversal_permutation(extended_evaluation_rbo)
extended_evaluation_times_zero = [a * b % BLS_MODULUS for a, b in zip(zero_poly_eval, extended_evaluation)]
extended_evaluations_fft = fft_field(extended_evaluation_times_zero, ROOTS_OF_UNITY2, inv=True)
extended_evaluations_fft = fft_field(extended_evaluation_times_zero, ROOTS_OF_UNITY_EXTENDED, inv=True)
shift_factor = PRIMITIVE_ROOT_OF_UNITY
shift_inv = div(1, PRIMITIVE_ROOT_OF_UNITY)
@ -514,22 +499,22 @@ def recover_samples_impl(samples: Sequence[Tuple[int, Sequence[BLSFieldElement]]
shifted_extended_evaluation = shift_polynomialcoeff(extended_evaluations_fft, shift_factor)
shifted_zero_poly = shift_polynomialcoeff(full_zero_poly, shift_factor)
eval_shifted_extended_evaluation = fft_field(shifted_extended_evaluation, ROOTS_OF_UNITY2)
eval_shifted_zero_poly = fft_field(shifted_zero_poly, ROOTS_OF_UNITY2)
eval_shifted_extended_evaluation = fft_field(shifted_extended_evaluation, ROOTS_OF_UNITY_EXTENDED)
eval_shifted_zero_poly = fft_field(shifted_zero_poly, ROOTS_OF_UNITY_EXTENDED)
eval_shifted_reconstructed_poly = [
div(a, b)
for a, b in zip(eval_shifted_extended_evaluation, eval_shifted_zero_poly)
]
shifted_reconstructed_poly = fft_field(eval_shifted_reconstructed_poly, ROOTS_OF_UNITY2, inv=True)
shifted_reconstructed_poly = fft_field(eval_shifted_reconstructed_poly, ROOTS_OF_UNITY_EXTENDED, inv=True)
reconstructed_poly = shift_polynomialcoeff(shifted_reconstructed_poly, shift_inv)
reconstructed_data = bit_reversal_permutation(fft_field(reconstructed_poly, ROOTS_OF_UNITY2))
reconstructed_data = bit_reversal_permutation(fft_field(reconstructed_poly, ROOTS_OF_UNITY_EXTENDED))
for sample_id, sample_data in samples:
assert reconstructed_data[sample_id * FIELD_ELEMENTS_PER_SAMPLE:(sample_id + 1) * FIELD_ELEMENTS_PER_SAMPLE] == \
assert reconstructed_data[sample_id * FIELD_ELEMENTS_PER_CELL:(sample_id + 1) * FIELD_ELEMENTS_PER_CELL] == \
sample_data
return reconstructed_data