2023-02-07 23:22:28 +00:00
# Deneb -- Polynomial Commitments
2022-06-22 12:13:41 +00:00
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE - RUN doctoc TO UPDATE -->
- [Introduction ](#introduction )
- [Custom types ](#custom-types )
- [Constants ](#constants )
- [Preset ](#preset )
2022-11-03 15:01:32 +00:00
- [Blob ](#blob )
2022-06-22 12:13:41 +00:00
- [Trusted setup ](#trusted-setup )
- [Helper functions ](#helper-functions )
2022-09-26 13:39:16 +00:00
- [Bit-reversal permutation ](#bit-reversal-permutation )
- [`is_power_of_two` ](#is_power_of_two )
- [`reverse_bits` ](#reverse_bits )
- [`bit_reversal_permutation` ](#bit_reversal_permutation )
2022-06-22 12:13:41 +00:00
- [BLS12-381 helpers ](#bls12-381-helpers )
2024-04-23 16:07:37 +00:00
- [`multi_exp` ](#multi_exp )
2022-12-01 12:59:00 +00:00
- [`hash_to_bls_field` ](#hash_to_bls_field )
2022-09-26 15:57:00 +00:00
- [`bytes_to_bls_field` ](#bytes_to_bls_field )
2024-02-01 17:45:02 +00:00
- [`bls_field_to_bytes` ](#bls_field_to_bytes )
2023-01-25 15:15:19 +00:00
- [`validate_kzg_g1` ](#validate_kzg_g1 )
- [`bytes_to_kzg_commitment` ](#bytes_to_kzg_commitment )
- [`bytes_to_kzg_proof` ](#bytes_to_kzg_proof )
2022-11-03 15:01:32 +00:00
- [`blob_to_polynomial` ](#blob_to_polynomial )
2023-01-29 13:05:02 +00:00
- [`compute_challenge` ](#compute_challenge )
2022-06-22 12:13:41 +00:00
- [`bls_modular_inverse` ](#bls_modular_inverse )
- [`div` ](#div )
2022-09-19 19:16:19 +00:00
- [`g1_lincomb` ](#g1_lincomb )
2022-11-03 15:01:32 +00:00
- [`compute_powers` ](#compute_powers )
2023-10-16 09:38:09 +00:00
- [`compute_roots_of_unity` ](#compute_roots_of_unity )
2022-11-03 15:01:32 +00:00
- [Polynomials ](#polynomials )
- [`evaluate_polynomial_in_evaluation_form` ](#evaluate_polynomial_in_evaluation_form )
2022-06-22 12:13:41 +00:00
- [KZG ](#kzg )
2022-07-13 10:13:30 +00:00
- [`blob_to_kzg_commitment` ](#blob_to_kzg_commitment )
2022-06-22 12:13:41 +00:00
- [`verify_kzg_proof` ](#verify_kzg_proof )
2022-11-09 12:01:47 +00:00
- [`verify_kzg_proof_impl` ](#verify_kzg_proof_impl )
2023-02-14 21:21:46 +00:00
- [`verify_kzg_proof_batch` ](#verify_kzg_proof_batch )
2022-07-13 10:13:30 +00:00
- [`compute_kzg_proof` ](#compute_kzg_proof )
2023-02-15 18:23:04 +00:00
- [`compute_quotient_eval_within_domain` ](#compute_quotient_eval_within_domain )
2023-01-24 13:02:22 +00:00
- [`compute_kzg_proof_impl` ](#compute_kzg_proof_impl )
2023-01-29 13:05:02 +00:00
- [`compute_blob_kzg_proof` ](#compute_blob_kzg_proof )
- [`verify_blob_kzg_proof` ](#verify_blob_kzg_proof )
2023-02-14 21:21:46 +00:00
- [`verify_blob_kzg_proof_batch` ](#verify_blob_kzg_proof_batch )
2022-06-22 12:13:41 +00:00
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
2023-06-08 07:05:46 +00:00
This document specifies basic polynomial operations and KZG polynomial commitment operations that are essential for the implementation of the EIP-4844 feature in the Deneb specification. The implementations are not optimized for performance, but readability. All practical implementations should optimize the polynomial operations.
2022-06-22 12:13:41 +00:00
2022-11-09 12:02:56 +00:00
Functions flagged as "Public method" MUST be provided by the underlying KZG library as public functions. All other functions are private functions used internally by the KZG library.
2023-01-25 15:15:19 +00:00
Public functions MUST accept raw bytes as input and perform the required cryptographic normalization before invoking any internal functions.
2022-06-22 12:13:41 +00:00
## Custom types
| Name | SSZ equivalent | Description |
| - | - | - |
2022-07-13 10:12:31 +00:00
| `G1Point` | `Bytes48` | |
| `G2Point` | `Bytes96` | |
2023-01-25 15:15:19 +00:00
| `BLSFieldElement` | `uint256` | Validation: `x < BLS_MODULUS` |
| `KZGCommitment` | `Bytes48` | Validation: Perform [BLS standard's ](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-04#section-2.5 ) "KeyValidate" check but do allow the identity point |
2022-06-22 12:13:41 +00:00
| `KZGProof` | `Bytes48` | Same as for `KZGCommitment` |
2023-01-24 14:14:25 +00:00
| `Polynomial` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | A polynomial in evaluation form |
2023-03-15 15:25:09 +00:00
| `Blob` | `ByteVector[BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB]` | A basic data blob |
2022-06-22 12:13:41 +00:00
## Constants
| Name | Value | Notes |
| - | - | - |
| `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` | Scalar field modulus of BLS12-381 |
2023-03-07 21:56:55 +00:00
| `BYTES_PER_COMMITMENT` | `uint64(48)` | The number of bytes in a KZG commitment |
| `BYTES_PER_PROOF` | `uint64(48)` | The number of bytes in a KZG proof |
2022-11-03 15:01:32 +00:00
| `BYTES_PER_FIELD_ELEMENT` | `uint64(32)` | Bytes used to encode a BLS scalar field element |
2023-03-07 21:56:55 +00:00
| `BYTES_PER_BLOB` | `uint64(BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB)` | The number of bytes in a blob |
2023-01-25 15:15:19 +00:00
| `G1_POINT_AT_INFINITY` | `Bytes48(b'\xc0' + b'\x00' * 47)` | Serialized form of the point at infinity on the G1 group |
2023-05-15 10:23:18 +00:00
| `KZG_ENDIANNESS` | `'big'` | The endianness of the field elements including blobs |
2023-12-04 01:12:07 +00:00
| `PRIMITIVE_ROOT_OF_UNITY` | `7` | The primitive root of unity from which all roots of unity should be derived |
2022-06-22 12:13:41 +00:00
## Preset
2022-11-03 15:01:32 +00:00
### Blob
| Name | Value |
| - | - |
| `FIELD_ELEMENTS_PER_BLOB` | `uint64(4096)` |
| `FIAT_SHAMIR_PROTOCOL_DOMAIN` | `b'FSBLOBVERIFY_V1_'` |
2023-02-14 21:21:46 +00:00
| `RANDOM_CHALLENGE_KZG_BATCH_DOMAIN` | `b'RCKZGBATCH___V1_'` |
2022-11-03 15:01:32 +00:00
2022-06-22 12:13:41 +00:00
### Trusted setup
| Name | Value |
| - | - |
2022-12-13 10:13:22 +00:00
| `KZG_SETUP_G2_LENGTH` | `65` |
2024-01-05 10:21:15 +00:00
| `KZG_SETUP_G1_MONOMIAL` | `Vector[G1Point, FIELD_ELEMENTS_PER_BLOB]` |
2023-10-17 15:59:27 +00:00
| `KZG_SETUP_G1_LAGRANGE` | `Vector[G1Point, FIELD_ELEMENTS_PER_BLOB]` |
2024-01-05 10:21:15 +00:00
| `KZG_SETUP_G2_MONOMIAL` | `Vector[G2Point, KZG_SETUP_G2_LENGTH]` |
2022-06-22 12:13:41 +00:00
## Helper functions
2022-09-26 13:39:16 +00:00
### Bit-reversal permutation
All polynomials (which are always given in Lagrange form) should be interpreted as being in
bit-reversal permutation. In practice, clients can implement this by storing the lists
2023-10-16 09:41:46 +00:00
`KZG_SETUP_G1_LAGRANGE` and roots of unity in bit-reversal permutation, so these functions only
2022-09-26 13:39:16 +00:00
have to be called once at startup.
#### `is_power_of_two`
```python
def is_power_of_two(value: int) -> bool:
"""
Check if ``value`` is a power of two integer.
"""
return (value > 0) and (value & (value - 1) == 0)
```
#### `reverse_bits`
```python
def reverse_bits(n: int, order: int) -> int:
"""
2022-11-03 15:01:32 +00:00
Reverse the bit order of an integer ``n``.
2022-09-26 13:39:16 +00:00
"""
assert is_power_of_two(order)
# Convert n to binary with the same number of bits as "order" - 1, then reverse its bit order
return int(('{:0' + str(order.bit_length() - 1) + 'b}').format(n)[::-1], 2)
```
#### `bit_reversal_permutation`
```python
2022-09-28 04:22:53 +00:00
def bit_reversal_permutation(sequence: Sequence[T]) -> Sequence[T]:
2022-09-26 13:39:16 +00:00
"""
2022-09-27 11:13:56 +00:00
Return a copy with bit-reversed permutation. The permutation is an involution (inverts itself).
2022-09-26 13:39:16 +00:00
The input and output are a sequence of generic type ``T`` objects.
"""
2022-09-28 04:22:53 +00:00
return [sequence[reverse_bits(i, len(sequence))] for i in range(len(sequence))]
2022-09-26 13:39:16 +00:00
```
2022-06-22 12:13:41 +00:00
### BLS12-381 helpers
2024-04-23 16:07:37 +00:00
#### `multi_exp`
This function performs a multi-scalar multiplication between `points` and `integers` . `points` can either be in G1 or G2.
```python
2024-04-23 16:21:45 +00:00
def multi_exp(points: Sequence[TPoint],
integers: Sequence[uint64]) -> Sequence[TPoint]:
2024-04-23 16:07:37 +00:00
# pylint: disable=unused-argument
...
```
2022-12-01 12:59:00 +00:00
#### `hash_to_bls_field`
```python
def hash_to_bls_field(data: bytes) -> BLSFieldElement:
"""
Hash ``data`` and convert the output to a BLS scalar field element.
The output is not uniform over the BLS field.
"""
hashed_data = hash(data)
2023-05-12 16:14:43 +00:00
return BLSFieldElement(int.from_bytes(hashed_data, KZG_ENDIANNESS) % BLS_MODULUS)
2022-12-01 12:59:00 +00:00
```
2022-09-26 15:57:00 +00:00
#### `bytes_to_bls_field`
```python
def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement:
"""
2023-01-25 15:15:19 +00:00
Convert untrusted bytes to a trusted and validated BLS scalar field element.
2022-12-01 12:59:00 +00:00
This function does not accept inputs greater than the BLS modulus.
2022-11-03 15:01:32 +00:00
"""
2023-05-12 16:14:43 +00:00
field_element = int.from_bytes(b, KZG_ENDIANNESS)
2022-12-01 12:59:00 +00:00
assert field_element < BLS_MODULUS
return BLSFieldElement(field_element)
2022-11-03 15:01:32 +00:00
```
2024-02-01 17:45:02 +00:00
#### `bls_field_to_bytes`
```python
def bls_field_to_bytes(x: BLSFieldElement) -> Bytes32:
return int.to_bytes(x % BLS_MODULUS, 32, KZG_ENDIANNESS)
```
2023-01-25 15:15:19 +00:00
#### `validate_kzg_g1`
```python
def validate_kzg_g1(b: Bytes48) -> None:
"""
Perform BLS validation required by the types `KZGProof` and `KZGCommitment` .
"""
if b == G1_POINT_AT_INFINITY:
return
assert bls.KeyValidate(b)
```
#### `bytes_to_kzg_commitment`
```python
def bytes_to_kzg_commitment(b: Bytes48) -> KZGCommitment:
"""
Convert untrusted bytes into a trusted and validated KZGCommitment.
"""
validate_kzg_g1(b)
return KZGCommitment(b)
```
#### `bytes_to_kzg_proof`
```python
def bytes_to_kzg_proof(b: Bytes48) -> KZGProof:
"""
Convert untrusted bytes into a trusted and validated KZGProof.
"""
validate_kzg_g1(b)
return KZGProof(b)
```
2022-11-03 15:01:32 +00:00
#### `blob_to_polynomial`
```python
def blob_to_polynomial(blob: Blob) -> Polynomial:
"""
Convert a blob to list of BLS field scalars.
"""
polynomial = Polynomial()
for i in range(FIELD_ELEMENTS_PER_BLOB):
2022-12-01 12:59:00 +00:00
value = bytes_to_bls_field(blob[i * BYTES_PER_FIELD_ELEMENT: (i + 1) * BYTES_PER_FIELD_ELEMENT])
2022-11-03 15:01:32 +00:00
polynomial[i] = value
return polynomial
```
2023-01-29 13:05:02 +00:00
#### `compute_challenge`
2022-11-03 15:01:32 +00:00
```python
2023-02-13 14:57:04 +00:00
def compute_challenge(blob: Blob,
2023-01-29 13:05:02 +00:00
commitment: KZGCommitment) -> BLSFieldElement:
2022-11-03 15:01:32 +00:00
"""
2023-02-14 21:10:09 +00:00
Return the Fiat-Shamir challenge required by the rest of the protocol.
2022-09-26 15:57:00 +00:00
"""
2023-01-29 13:05:02 +00:00
2023-02-15 19:48:58 +00:00
# Append the degree of the polynomial as a domain separator
2023-05-12 16:14:43 +00:00
degree_poly = int.to_bytes(FIELD_ELEMENTS_PER_BLOB, 16, KZG_ENDIANNESS)
2023-02-15 19:48:58 +00:00
data = FIAT_SHAMIR_PROTOCOL_DOMAIN + degree_poly
2022-11-03 15:01:32 +00:00
2023-02-13 14:57:04 +00:00
data += blob
2023-01-29 13:05:02 +00:00
data += commitment
2022-11-03 15:01:32 +00:00
2023-02-15 19:48:58 +00:00
# Transcript has been prepared: time to create the challenge
2023-02-14 20:17:25 +00:00
return hash_to_bls_field(data)
2022-09-26 15:57:00 +00:00
```
2022-06-22 12:13:41 +00:00
#### `bls_modular_inverse`
```python
def bls_modular_inverse(x: BLSFieldElement) -> BLSFieldElement:
"""
2023-03-16 13:49:40 +00:00
Compute the modular inverse of x (for x != 0)
i.e. return y such that x * y % BLS_MODULUS == 1
2022-06-22 12:13:41 +00:00
"""
2023-03-16 13:49:40 +00:00
assert (int(x) % BLS_MODULUS) != 0
return BLSFieldElement(pow(x, -1, BLS_MODULUS))
2022-06-22 12:13:41 +00:00
```
#### `div`
```python
2022-06-23 10:40:09 +00:00
def div(x: BLSFieldElement, y: BLSFieldElement) -> BLSFieldElement:
2022-11-03 15:01:32 +00:00
"""
Divide two field elements: ``x`` by `y` `.
"""
2022-11-28 12:16:18 +00:00
return BLSFieldElement((int(x) * int(bls_modular_inverse(y))) % BLS_MODULUS)
2022-06-22 12:13:41 +00:00
```
2022-09-19 19:16:19 +00:00
#### `g1_lincomb`
2022-06-22 12:13:41 +00:00
```python
2022-09-19 19:16:19 +00:00
def g1_lincomb(points: Sequence[KZGCommitment], scalars: Sequence[BLSFieldElement]) -> KZGCommitment:
2022-06-22 12:13:41 +00:00
"""
2024-04-23 13:21:54 +00:00
BLS multiscalar multiplication in G1. This can be naively implemented using double-and-add.
2022-06-22 12:13:41 +00:00
"""
2022-07-13 10:12:31 +00:00
assert len(points) == len(scalars)
2024-04-23 13:55:37 +00:00
if len(points) == 0:
2024-04-23 14:11:46 +00:00
return bls.G1_to_bytes48(bls.Z1())
2024-04-23 13:55:37 +00:00
2024-04-23 11:44:50 +00:00
points_g1 = []
for point in points:
points_g1.append(bls.bytes48_to_G1(point))
2024-04-23 13:55:37 +00:00
2024-04-23 13:21:54 +00:00
result = bls.multi_exp(points_g1, scalars)
2022-07-13 10:12:31 +00:00
return KZGCommitment(bls.G1_to_bytes48(result))
```
2022-11-03 15:01:32 +00:00
#### `compute_powers`
```python
def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]:
"""
2022-11-24 13:11:46 +00:00
Return ``x`` to power of [0, n-1], if n > 0. When n==0, an empty array is returned.
2022-11-03 15:01:32 +00:00
"""
current_power = 1
powers = []
for _ in range(n):
powers.append(BLSFieldElement(current_power))
current_power = current_power * int(x) % BLS_MODULUS
return powers
```
2023-10-16 09:38:09 +00:00
#### `compute_roots_of_unity`
```python
def compute_roots_of_unity(order: uint64) -> Sequence[BLSFieldElement]:
"""
Return roots of unity of ``order``.
"""
assert (BLS_MODULUS - 1) % int(order) == 0
2023-10-16 13:11:59 +00:00
root_of_unity = BLSFieldElement(pow(PRIMITIVE_ROOT_OF_UNITY, (BLS_MODULUS - 1) // int(order), BLS_MODULUS))
2023-10-16 09:38:09 +00:00
return compute_powers(root_of_unity, order)
```
2022-11-23 14:52:47 +00:00
2022-11-03 15:01:32 +00:00
### Polynomials
#### `evaluate_polynomial_in_evaluation_form`
```python
def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial,
z: BLSFieldElement) -> BLSFieldElement:
"""
2023-03-04 19:20:01 +00:00
Evaluate a polynomial (in evaluation form) at an arbitrary point ``z``.
- When ``z`` is in the domain, the evaluation can be found by indexing the polynomial at the
position that ``z`` is in the domain.
- When ``z`` is not in the domain, the barycentric formula is used:
2022-11-03 15:01:32 +00:00
f(z) = (z**WIDTH - 1) / WIDTH * sum_(i=0)^WIDTH (f(DOMAIN[i]) * DOMAIN[i]) / (z - DOMAIN[i])
"""
width = len(polynomial)
assert width == FIELD_ELEMENTS_PER_BLOB
2022-11-28 12:16:18 +00:00
inverse_width = bls_modular_inverse(BLSFieldElement(width))
2022-11-03 15:01:32 +00:00
2023-10-16 09:38:09 +00:00
roots_of_unity_brp = bit_reversal_permutation(compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB))
2022-11-03 15:01:32 +00:00
2023-01-16 13:57:35 +00:00
# If we are asked to evaluate within the domain, we already know the answer
if z in roots_of_unity_brp:
eval_index = roots_of_unity_brp.index(z)
return BLSFieldElement(polynomial[eval_index])
2022-11-03 15:01:32 +00:00
result = 0
for i in range(width):
2022-11-28 12:16:18 +00:00
a = BLSFieldElement(int(polynomial[i]) * int(roots_of_unity_brp[i]) % BLS_MODULUS)
b = BLSFieldElement((int(BLS_MODULUS) + int(z) - int(roots_of_unity_brp[i])) % BLS_MODULUS)
result += int(div(a, b) % BLS_MODULUS)
2023-03-02 20:49:10 +00:00
result = result * int(BLS_MODULUS + pow(z, width, BLS_MODULUS) - 1) * int(inverse_width)
2022-11-28 12:16:18 +00:00
return BLSFieldElement(result % BLS_MODULUS)
2022-11-03 15:01:32 +00:00
```
2022-06-22 12:13:41 +00:00
### KZG
2023-06-08 07:05:46 +00:00
KZG core functions. These are also defined in Deneb execution specs.
2022-06-22 12:13:41 +00:00
2022-07-13 10:12:31 +00:00
#### `blob_to_kzg_commitment`
2022-06-22 12:13:41 +00:00
```python
2022-07-13 10:12:31 +00:00
def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment:
2022-11-09 12:02:56 +00:00
"""
Public method.
"""
2023-03-07 21:56:55 +00:00
assert len(blob) == BYTES_PER_BLOB
2023-10-16 09:41:46 +00:00
return g1_lincomb(bit_reversal_permutation(KZG_SETUP_G1_LAGRANGE), blob_to_polynomial(blob))
2022-06-22 12:13:41 +00:00
```
#### `verify_kzg_proof`
```python
2023-01-25 15:15:19 +00:00
def verify_kzg_proof(commitment_bytes: Bytes48,
2023-03-07 21:56:55 +00:00
z_bytes: Bytes32,
y_bytes: Bytes32,
2023-01-25 15:15:19 +00:00
proof_bytes: Bytes48) -> bool:
2022-06-23 10:40:09 +00:00
"""
2022-07-13 10:12:31 +00:00
Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomial_kzg``.
2022-11-09 12:01:47 +00:00
Receives inputs as bytes.
Public method.
"""
2023-03-07 21:56:55 +00:00
assert len(commitment_bytes) == BYTES_PER_COMMITMENT
assert len(z_bytes) == BYTES_PER_FIELD_ELEMENT
assert len(y_bytes) == BYTES_PER_FIELD_ELEMENT
assert len(proof_bytes) == BYTES_PER_PROOF
2023-01-25 15:15:19 +00:00
return verify_kzg_proof_impl(bytes_to_kzg_commitment(commitment_bytes),
2023-03-07 21:56:55 +00:00
bytes_to_bls_field(z_bytes),
bytes_to_bls_field(y_bytes),
2023-01-25 15:15:19 +00:00
bytes_to_kzg_proof(proof_bytes))
2022-11-09 12:01:47 +00:00
```
#### `verify_kzg_proof_impl`
```python
2023-01-25 15:15:19 +00:00
def verify_kzg_proof_impl(commitment: KZGCommitment,
2022-11-09 12:01:47 +00:00
z: BLSFieldElement,
y: BLSFieldElement,
2023-01-25 15:15:19 +00:00
proof: KZGProof) -> bool:
2022-11-09 12:01:47 +00:00
"""
Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomial_kzg``.
2022-06-23 10:40:09 +00:00
"""
2022-07-13 10:12:31 +00:00
# Verify: P - y = Q * (X - z)
2023-10-16 09:41:46 +00:00
X_minus_z = bls.add(
bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[1]),
bls.multiply(bls.G2(), (BLS_MODULUS - z) % BLS_MODULUS),
)
2023-03-02 20:49:10 +00:00
P_minus_y = bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), (BLS_MODULUS - y) % BLS_MODULUS))
2022-06-22 12:13:41 +00:00
return bls.pairing_check([
2023-03-02 20:49:10 +00:00
[P_minus_y, bls.neg(bls.G2())],
2023-01-25 15:15:19 +00:00
[bls.bytes48_to_G1(proof), X_minus_z]
2022-06-22 12:13:41 +00:00
])
```
2023-02-14 21:21:46 +00:00
#### `verify_kzg_proof_batch`
2023-01-27 14:23:38 +00:00
```python
2023-02-14 21:21:46 +00:00
def verify_kzg_proof_batch(commitments: Sequence[KZGCommitment],
2023-01-27 14:23:38 +00:00
zs: Sequence[BLSFieldElement],
ys: Sequence[BLSFieldElement],
proofs: Sequence[KZGProof]) -> bool:
"""
Verify multiple KZG proofs efficiently.
"""
assert len(commitments) == len(zs) == len(ys) == len(proofs)
# Compute a random challenge. Note that it does not have to be computed from a hash,
# r just has to be random.
2023-05-12 16:14:43 +00:00
degree_poly = int.to_bytes(FIELD_ELEMENTS_PER_BLOB, 8, KZG_ENDIANNESS)
num_commitments = int.to_bytes(len(commitments), 8, KZG_ENDIANNESS)
2023-02-14 21:21:46 +00:00
data = RANDOM_CHALLENGE_KZG_BATCH_DOMAIN + degree_poly + num_commitments
2023-01-27 14:23:38 +00:00
2023-02-14 20:00:58 +00:00
# Append all inputs to the transcript before we hash
2023-01-27 14:23:38 +00:00
for commitment, z, y, proof in zip(commitments, zs, ys, proofs):
data += commitment \
2023-05-12 16:14:43 +00:00
+ int.to_bytes(z, BYTES_PER_FIELD_ELEMENT, KZG_ENDIANNESS) \
+ int.to_bytes(y, BYTES_PER_FIELD_ELEMENT, KZG_ENDIANNESS) \
2023-01-27 14:23:38 +00:00
+ proof
2023-02-14 20:17:25 +00:00
r = hash_to_bls_field(data)
2023-01-27 14:23:38 +00:00
r_powers = compute_powers(r, len(commitments))
# Verify: e(sum r^i proof_i, [s]) ==
# e(sum r^i (commitment_i - [y_i]) + sum r^i z_i proof_i, [1])
proof_lincomb = g1_lincomb(proofs, r_powers)
2023-02-16 14:09:57 +00:00
proof_z_lincomb = g1_lincomb(
proofs,
[BLSFieldElement((int(z) * int(r_power)) % BLS_MODULUS) for z, r_power in zip(zs, r_powers)],
)
2023-03-02 20:49:10 +00:00
C_minus_ys = [bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), (BLS_MODULUS - y) % BLS_MODULUS))
2023-01-27 14:23:38 +00:00
for commitment, y in zip(commitments, ys)]
2023-02-14 20:59:24 +00:00
C_minus_y_as_KZGCommitments = [KZGCommitment(bls.G1_to_bytes48(x)) for x in C_minus_ys]
2023-01-27 14:23:38 +00:00
C_minus_y_lincomb = g1_lincomb(C_minus_y_as_KZGCommitments, r_powers)
return bls.pairing_check([
2023-10-16 09:41:46 +00:00
[bls.bytes48_to_G1(proof_lincomb), bls.neg(bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[1]))],
2023-03-02 20:49:10 +00:00
[bls.add(bls.bytes48_to_G1(C_minus_y_lincomb), bls.bytes48_to_G1(proof_z_lincomb)), bls.G2()]
2023-01-27 14:23:38 +00:00
])
```
2022-07-13 10:12:31 +00:00
#### `compute_kzg_proof`
```python
2023-03-07 21:56:55 +00:00
def compute_kzg_proof(blob: Blob, z_bytes: Bytes32) -> Tuple[KZGProof, Bytes32]:
2022-09-26 13:39:16 +00:00
"""
2023-01-24 13:02:22 +00:00
Compute KZG proof at point `z` for the polynomial represented by `blob` .
2022-12-17 17:14:48 +00:00
Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z).
Public method.
2022-09-26 13:39:16 +00:00
"""
2023-03-07 21:56:55 +00:00
assert len(blob) == BYTES_PER_BLOB
assert len(z_bytes) == BYTES_PER_FIELD_ELEMENT
2023-01-24 13:02:22 +00:00
polynomial = blob_to_polynomial(blob)
2023-03-07 21:56:55 +00:00
proof, y = compute_kzg_proof_impl(polynomial, bytes_to_bls_field(z_bytes))
2023-05-12 16:14:43 +00:00
return proof, y.to_bytes(BYTES_PER_FIELD_ELEMENT, KZG_ENDIANNESS)
2023-01-24 13:02:22 +00:00
```
2023-02-14 12:50:44 +00:00
#### `compute_quotient_eval_within_domain`
```python
def compute_quotient_eval_within_domain(z: BLSFieldElement,
polynomial: Polynomial,
y: BLSFieldElement
) -> BLSFieldElement:
"""
Given `y == p(z)` for a polynomial `p(x)` , compute `q(z)` : the KZG quotient polynomial evaluated at `z` for the
2023-10-16 09:38:09 +00:00
special case where `z` is in roots of unity.
2023-02-14 12:50:44 +00:00
For more details, read https://dankradfeist.de/ethereum/2021/06/18/pcs-multiproofs.html section "Dividing
when one of the points is zero". The code below computes q(x_m) for the roots of unity special case.
"""
2023-10-16 09:38:09 +00:00
roots_of_unity_brp = bit_reversal_permutation(compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB))
2023-02-14 12:50:44 +00:00
result = 0
for i, omega_i in enumerate(roots_of_unity_brp):
if omega_i == z: # skip the evaluation point in the sum
continue
f_i = int(BLS_MODULUS) + int(polynomial[i]) - int(y) % BLS_MODULUS
numerator = f_i * int(omega_i) % BLS_MODULUS
denominator = int(z) * (int(BLS_MODULUS) + int(z) - int(omega_i)) % BLS_MODULUS
2023-02-16 17:22:11 +00:00
result += int(div(BLSFieldElement(numerator), BLSFieldElement(denominator)))
2023-02-14 12:50:44 +00:00
return BLSFieldElement(result % BLS_MODULUS)
```
2023-01-24 13:02:22 +00:00
#### `compute_kzg_proof_impl`
```python
2023-03-07 17:50:56 +00:00
def compute_kzg_proof_impl(polynomial: Polynomial, z: BLSFieldElement) -> Tuple[KZGProof, BLSFieldElement]:
2023-01-24 13:02:22 +00:00
"""
2023-02-20 10:54:16 +00:00
Helper function for `compute_kzg_proof()` and `compute_blob_kzg_proof()` .
2023-01-24 13:02:22 +00:00
"""
2023-10-16 09:38:09 +00:00
roots_of_unity_brp = bit_reversal_permutation(compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB))
2023-02-14 12:50:44 +00:00
# For all x_i, compute p(x_i) - p(z)
2022-07-13 10:12:31 +00:00
y = evaluate_polynomial_in_evaluation_form(polynomial, z)
2022-11-28 12:16:18 +00:00
polynomial_shifted = [BLSFieldElement((int(p) - int(y)) % BLS_MODULUS) for p in polynomial]
2022-07-13 10:12:31 +00:00
2023-02-14 12:50:44 +00:00
# For all x_i, compute (x_i - z)
2022-11-28 12:16:18 +00:00
denominator_poly = [BLSFieldElement((int(x) - int(z)) % BLS_MODULUS)
2023-02-20 10:57:39 +00:00
for x in roots_of_unity_brp]
2022-07-13 10:12:31 +00:00
2023-02-14 12:50:44 +00:00
# Compute the quotient polynomial directly in evaluation form
quotient_polynomial = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_BLOB
for i, (a, b) in enumerate(zip(polynomial_shifted, denominator_poly)):
if b == 0:
# The denominator is zero hence `z` is a root of unity: we must handle it as a special case
quotient_polynomial[i] = compute_quotient_eval_within_domain(roots_of_unity_brp[i], polynomial, y)
else:
# Compute: q(x_i) = (p(x_i) - p(z)) / (x_i - z).
quotient_polynomial[i] = div(a, b)
2023-10-16 09:41:46 +00:00
return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_G1_LAGRANGE), quotient_polynomial)), y
2022-07-13 10:12:31 +00:00
```
2023-01-29 13:05:02 +00:00
#### `compute_blob_kzg_proof`
2022-06-22 12:13:41 +00:00
```python
2023-03-07 17:50:56 +00:00
def compute_blob_kzg_proof(blob: Blob, commitment_bytes: Bytes48) -> KZGProof:
2022-06-22 12:13:41 +00:00
"""
2023-01-29 13:05:02 +00:00
Given a blob, return the KZG proof that is used to verify it against the commitment.
2023-03-07 17:50:56 +00:00
This method does not verify that the commitment is correct with respect to `blob` .
2022-11-09 12:02:56 +00:00
Public method.
"""
2023-03-07 21:56:55 +00:00
assert len(blob) == BYTES_PER_BLOB
assert len(commitment_bytes) == BYTES_PER_COMMITMENT
2023-03-07 17:50:56 +00:00
commitment = bytes_to_kzg_commitment(commitment_bytes)
2023-01-29 13:05:02 +00:00
polynomial = blob_to_polynomial(blob)
2023-02-13 14:57:04 +00:00
evaluation_challenge = compute_challenge(blob, commitment)
2023-03-07 17:50:56 +00:00
proof, _ = compute_kzg_proof_impl(polynomial, evaluation_challenge)
return proof
2022-06-22 12:13:41 +00:00
```
2022-07-13 10:12:31 +00:00
2023-01-29 13:05:02 +00:00
#### `verify_blob_kzg_proof`
2022-11-03 15:01:32 +00:00
```python
2023-01-29 13:05:02 +00:00
def verify_blob_kzg_proof(blob: Blob,
commitment_bytes: Bytes48,
proof_bytes: Bytes48) -> bool:
2022-11-09 12:02:56 +00:00
"""
2023-01-29 13:05:02 +00:00
Given a blob and a KZG proof, verify that the blob data corresponds to the provided commitment.
2022-12-01 12:59:00 +00:00
2022-11-09 12:02:56 +00:00
Public method.
"""
2023-03-07 21:56:55 +00:00
assert len(blob) == BYTES_PER_BLOB
assert len(commitment_bytes) == BYTES_PER_COMMITMENT
assert len(proof_bytes) == BYTES_PER_PROOF
2023-01-29 13:05:02 +00:00
commitment = bytes_to_kzg_commitment(commitment_bytes)
2023-01-27 14:23:38 +00:00
2023-01-29 13:05:02 +00:00
polynomial = blob_to_polynomial(blob)
2023-02-13 14:57:04 +00:00
evaluation_challenge = compute_challenge(blob, commitment)
2023-01-27 14:23:38 +00:00
2023-02-14 20:59:41 +00:00
# Evaluate polynomial at `evaluation_challenge`
2023-01-29 13:05:02 +00:00
y = evaluate_polynomial_in_evaluation_form(polynomial, evaluation_challenge)
2023-01-27 14:23:38 +00:00
2023-01-29 13:05:02 +00:00
# Verify proof
proof = bytes_to_kzg_proof(proof_bytes)
return verify_kzg_proof_impl(commitment, evaluation_challenge, y, proof)
2022-11-03 15:01:32 +00:00
```
2023-01-27 14:23:38 +00:00
2023-02-14 21:21:46 +00:00
#### `verify_blob_kzg_proof_batch`
2023-01-27 14:23:38 +00:00
```python
2023-02-14 21:21:46 +00:00
def verify_blob_kzg_proof_batch(blobs: Sequence[Blob],
2023-01-29 13:05:02 +00:00
commitments_bytes: Sequence[Bytes48],
proofs_bytes: Sequence[Bytes48]) -> bool:
2023-01-27 14:23:38 +00:00
"""
2023-01-29 13:05:02 +00:00
Given a list of blobs and blob KZG proofs, verify that they correspond to the provided commitments.
2023-05-17 16:24:48 +00:00
Will return True if there are zero blobs/commitments/proofs.
2023-01-27 14:23:38 +00:00
Public method.
"""
2023-02-14 21:10:09 +00:00
assert len(blobs) == len(commitments_bytes) == len(proofs_bytes)
2024-01-15 15:34:28 +00:00
2023-01-29 13:05:02 +00:00
commitments, evaluation_challenges, ys, proofs = [], [], [], []
for blob, commitment_bytes, proof_bytes in zip(blobs, commitments_bytes, proofs_bytes):
2023-03-07 21:56:55 +00:00
assert len(blob) == BYTES_PER_BLOB
assert len(commitment_bytes) == BYTES_PER_COMMITMENT
assert len(proof_bytes) == BYTES_PER_PROOF
2023-01-29 13:05:02 +00:00
commitment = bytes_to_kzg_commitment(commitment_bytes)
commitments.append(commitment)
polynomial = blob_to_polynomial(blob)
2023-02-13 14:57:04 +00:00
evaluation_challenge = compute_challenge(blob, commitment)
2023-02-13 14:32:50 +00:00
evaluation_challenges.append(evaluation_challenge)
2023-01-29 13:05:02 +00:00
ys.append(evaluate_polynomial_in_evaluation_form(polynomial, evaluation_challenge))
proofs.append(bytes_to_kzg_proof(proof_bytes))
2023-01-27 14:23:38 +00:00
2023-02-14 21:21:46 +00:00
return verify_kzg_proof_batch(commitments, evaluation_challenges, ys, proofs)
2023-01-27 14:23:38 +00:00
```