Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronize EIP-4844 cryptography with consensus specs #5649

Merged
merged 1 commit into from
Oct 3, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 20 additions & 106 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/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/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/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/polynomial-commitments.md):
- [`verify_kzg_proof()`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/polynomial-commitments.md#verify_kzg_proof)
- [`evaluate_polynomial_in_evaluation_form()`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/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/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/validator.md):
- [`hash_to_bls_field()`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/validator.md#hash_to_bls_field)
- [`compute_powers()`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/validator.md#compute_powers)
- [`compute_aggregated_poly_and_commitment()`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/validator.md#compute_aggregated_poly_and_commitment)
- [`PolynomialAndCommitment`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/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,53 +315,23 @@ 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
blobs = wrapper.blobs
# note: assert blobs are not malformatted

assert len(versioned_hashes) == len(commitments) == len(blobs)
number_of_blobs = len(blobs)

# Generate random linear combination challenges
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
Expand Down