Skip to content

Commit

Permalink
Synchronize EIP-4844 cryptography with consensus specs
Browse files Browse the repository at this point in the history
This commit introduces various fixes and improvements to the KZG-related code of EIP-4844. This commit brings the EIP
code up-to-speed with the consensus-specs code.

Note that instead of updating the cryptographic functions we instead link to the relevant parts of the consensus-specs
repository. This is done to avoid code duplication between the two repositories -- an approach that seems prudent in
cross-execution-and-consensus situations like EIP-4844.
  • Loading branch information
asn-d6 committed Sep 12, 2022
1 parent b31b9d8 commit 651cbec
Showing 1 changed file with 21 additions and 101 deletions.
122 changes: 21 additions & 101 deletions EIPS/eip-4844.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ Compared to full data sharding, this EIP has a reduced cap on the number of thes
| `BLOB_TX_TYPE` | `Bytes1(0x05)` |
| `FIELD_ELEMENTS_PER_BLOB` | `4096` |
| `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` |
| `KZG_SETUP_G2` | `Vector[G2Point, FIELD_ELEMENTS_PER_BLOB]`, contents TBD |
| `KZG_SETUP_LAGRANGE` | `Vector[KZGCommitment, FIELD_ELEMENTS_PER_BLOB]`, contents TBD |
| `ROOTS_OF_UNITY` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` |
| `BLOB_COMMITMENT_VERSION_KZG` | `Bytes1(0x01)` |
| `POINT_EVALUATION_PRECOMPILE_ADDRESS` | `Bytes20(0x14)` |
| `POINT_EVALUATION_PRECOMPILE_GAS` | `50000` |
Expand Down Expand Up @@ -72,80 +69,27 @@ Compared to full data sharding, this EIP has a reduced cap on the number of thes
| `KZGCommitment` | `Bytes48` | Same as BLS standard "is valid pubkey" check but also allows `0x00..00` for point-at-infinity |
| `KZGProof` | `Bytes48` | Same as for `KZGCommitment` |

### Helpers
### Cryptographic Helpers

Converts a blob to its corresponding KZG point:
Throughout this proposal we use cryptographic methods and classes defined in the corresponding [consensus 4844 specs](https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844).

```python
def lincomb(points: List[KZGCommitment], scalars: List[BLSFieldElement]) -> KZGCommitment:
"""
BLS multiscalar multiplication. This function can be optimized using Pippenger's algorithm and variants.
"""
r = bls.Z1
for x, a in zip(points, scalars):
r = bls.add(r, bls.multiply(x, a))
return r

def blob_to_kzg(blob: Blob) -> KZGCommitment:
return lincomb(KZG_SETUP_LAGRANGE, blob)
```
Specifically, we use the following methods from [`polynomial-commitments.md`](https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md):
- [`verify_kzg_proof()`](https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#verify_kzg_proof)
- [`evaluate_polynomial_in_evaluation_form()`](https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#evaluate_polynomial_in_evaluation_form)

Converts a KZG point into a versioned hash:
We also use the following methods and classes from [`validator.md`](https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/validator.md):
- [`hash_to_bls_field()`](https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/validator.md#hash_to_bls_field)
- [`compute_powers()`](https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/validator.md#compute_powers)
- [`compute_aggregated_poly_and_commitment()`](https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/validator.md#compute_aggregated_poly_and_commitment)
- [`PolynomialAndCommitment`](https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/validator.md#PolynomialAndCommitment)

### Helpers

```python
def kzg_to_versioned_hash(kzg: KZGCommitment) -> VersionedHash:
return BLOB_COMMITMENT_VERSION_KZG + hash(kzg)[1:]
```

Verifies a KZG evaluation proof:

```python
def verify_kzg_proof(polynomial_kzg: KZGCommitment,
x: BLSFieldElement,
y: BLSFieldElement,
quotient_kzg: KZGProof) -> bool:
# Verify: P - y = Q * (X - x)
X_minus_x = bls.add(KZG_SETUP_G2[1], bls.multiply(bls.G2, BLS_MODULUS - x))
P_minus_y = bls.add(polynomial_kzg, bls.multiply(bls.G1, BLS_MODULUS - y))
return bls.pairing_check([
[P_minus_y, bls.neg(bls.G2)],
[quotient_kzg, X_minus_x]
])
```

Efficiently evaluates a polynomial in evaluation form using the barycentric formula

```python
def bls_modular_inverse(x: BLSFieldElement) -> BLSFieldElement:
"""
Compute the modular inverse of x
i.e. return y such that x * y % BLS_MODULUS == 1 and return 0 for x == 0
"""
return pow(x, -1, BLS_MODULUS) if x != 0 else 0


def div(x, y):
"""Divide two field elements: `x` by `y`"""
return x * bls_modular_inverse(y) % BLS_MODULUS


def evaluate_polynomial_in_evaluation_form(poly: List[BLSFieldElement], x: BLSFieldElement) -> BLSFieldElement:
"""
Evaluate a polynomial (in evaluation form) at an arbitrary point `x`
Uses the barycentric formula:
f(x) = (1 - x**WIDTH) / WIDTH * sum_(i=0)^WIDTH (f(DOMAIN[i]) * DOMAIN[i]) / (x - DOMAIN[i])
"""
width = len(poly)
assert width == FIELD_ELEMENTS_PER_BLOB
inverse_width = bls_modular_inverse(width)

for i in range(width):
r += div(poly[i] * ROOTS_OF_UNITY[i], (x - ROOTS_OF_UNITY[i]) )
r = r * (pow(x, width, BLS_MODULUS) - 1) * inverse_width % BLS_MODULUS

return r
```

Approximates `2 ** (numerator / denominator)`, with the simplest possible approximation that is continuous and has a continuous derivative:

```python
Expand Down Expand Up @@ -371,32 +315,6 @@ class BlobTransactionNetworkWrapper(Container):
We do network-level validation of `BlobTransactionNetworkWrapper` objects as follows:

```python
def hash_to_bls_field(x: Container) -> BLSFieldElement:
"""
This function is used to generate Fiat-Shamir challenges. The output is not uniform over the BLS field.
"""
return int.from_bytes(hash_tree_root(x), "little") % BLS_MODULUS


def compute_powers(x: BLSFieldElement, n: uint64) -> List[BLSFieldElement]:
current_power = 1
powers = []
for _ in range(n):
powers.append(BLSFieldElement(current_power))
current_power = current_power * int(x) % BLS_MODULUS
return powers

def vector_lincomb(vectors: List[List[BLSFieldElement]], scalars: List[BLSFieldElement]) -> List[BLSFieldElement]:
"""
Given a list of vectors, compute the linear combination of each column with `scalars`, and return the resulting
vector.
"""
r = [0]*len(vectors[0])
for v, a in zip(vectors, scalars):
for i, x in enumerate(v):
r[i] = (r[i] + a * x) % BLS_MODULUS
return [BLSFieldElement(x) for x in r]

def validate_blob_transaction_wrapper(wrapper: BlobTransactionNetworkWrapper):
versioned_hashes = wrapper.tx.message.blob_versioned_hashes
commitments = wrapper.blob_kzgs
Expand All @@ -410,18 +328,20 @@ def validate_blob_transaction_wrapper(wrapper: BlobTransactionNetworkWrapper):
r = hash_to_bls_field([blobs, commitments])
r_powers = compute_powers(r, number_of_blobs)

# Compute commitment to aggregated polynomial
aggregated_poly_commitment = lincomb(commitments, r_powers)

# Create aggregated polynomial in evaluation form
aggregated_poly = vector_lincomb(blobs, r_powers)
aggregated_poly, aggregated_poly_commitment = compute_aggregated_poly_and_commitment(
blobs,
commitments,
)

# Generate challenge `x` and evaluate the aggregated polynomial at `x`
x = hash_to_bls_field([aggregated_poly, aggregated_poly_commitment])
x = hash_to_bls_field(
PolynomialAndCommitment(polynomial=aggregated_poly, kzg_commitment=aggregated_poly_commitment)
)
# Evaluate aggregated polynomial at `x` (evaluation function checks for div-by-zero)
y = evaluate_polynomial_in_evaluation_form(aggregated_poly, x)

# Verify aggregated proof
assert verify_kzg_proof(aggregated_poly_commitment, x, y, wrapper.kzg_aggregated_proof)
assert verify_kzg_proof(aggregated_poly_commitment, x, y, kzg_aggregated_proof)

# Now that all commitments have been verified, check that versioned_hashes matches the commitments
for versioned_hash, commitment in zip(versioned_hashes, commitments):
Expand Down

0 comments on commit 651cbec

Please sign in to comment.