diff --git a/specs/eip4844/beacon-chain.md b/specs/eip4844/beacon-chain.md index 2aa5723e6..537b294c7 100644 --- a/specs/eip4844/beacon-chain.md +++ b/specs/eip4844/beacon-chain.md @@ -12,18 +12,13 @@ - [Custom types](#custom-types) - [Constants](#constants) - [Domain types](#domain-types) -- [Preset](#preset) - - [Trusted setup](#trusted-setup) - [Configuration](#configuration) - [Containers](#containers) - [Extended containers](#extended-containers) - [`BeaconBlockBody`](#beaconblockbody) - [Helper functions](#helper-functions) - - [KZG core](#kzg-core) - - [`lincomb`](#lincomb) - - [`blob_to_kzg`](#blob_to_kzg) - - [`kzg_to_versioned_hash`](#kzg_to_versioned_hash) - [Misc](#misc) + - [`kzg_to_versioned_hash`](#kzg_to_versioned_hash) - [`tx_peek_blob_versioned_hashes`](#tx_peek_blob_versioned_hashes) - [`verify_kzgs_against_transactions`](#verify_kzgs_against_transactions) - [Beacon chain state transition function](#beacon-chain-state-transition-function) @@ -42,11 +37,8 @@ This upgrade adds blobs to the beacon chain as part of EIP-4844. | Name | SSZ equivalent | Description | | - | - | - | -| `BLSFieldElement` | `uint256` | `x < BLS_MODULUS` | | `Blob` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | | | `VersionedHash` | `Bytes32` | | -| `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` | ## Constants @@ -54,8 +46,6 @@ This upgrade adds blobs to the beacon chain as part of EIP-4844. | - | - | | `BLOB_TX_TYPE` | `uint8(0x05)` | | `FIELD_ELEMENTS_PER_BLOB` | `4096` | -| `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` | -| `ROOTS_OF_UNITY` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | ### Domain types @@ -63,18 +53,6 @@ This upgrade adds blobs to the beacon chain as part of EIP-4844. | - | - | | `DOMAIN_BLOBS_SIDECAR` | `DomainType('0x0a000000')` | -## Preset - -### Trusted setup - -The trusted setup is part of the preset: during testing a `minimal` insecure variant may be used, -but reusing the `mainnet` settings in public networks is a critical security requirement. - -| Name | Value | -| - | - | -| `KZG_SETUP_G2` | `Vector[G2Point, FIELD_ELEMENTS_PER_BLOB]`, contents TBD | -| `KZG_SETUP_LAGRANGE` | `Vector[KZGCommitment, FIELD_ELEMENTS_PER_BLOB]`, contents TBD | - ## Configuration @@ -105,29 +83,7 @@ class BeaconBlockBody(Container): ## Helper functions -### KZG core - -KZG core functions. These are also defined in EIP-4844 execution specs. - -#### `lincomb` - -```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 -``` - -#### `blob_to_kzg` - -```python -def blob_to_kzg(blob: Blob) -> KZGCommitment: - return lincomb(blob, KZG_SETUP_LAGRANGE) -``` +### Misc #### `kzg_to_versioned_hash` @@ -136,8 +92,6 @@ def kzg_to_versioned_hash(kzg: KZGCommitment) -> VersionedHash: return BLOB_COMMITMENT_VERSION_KZG + hash(kzg)[1:] ``` -### Misc - #### `tx_peek_blob_versioned_hashes` This function retrieves the hashes from the `SignedBlobTransaction` as defined in EIP-4844, using SSZ offsets. diff --git a/specs/eip4844/polynomial-commitments.md b/specs/eip4844/polynomial-commitments.md new file mode 100644 index 000000000..df5aad146 --- /dev/null +++ b/specs/eip4844/polynomial-commitments.md @@ -0,0 +1,153 @@ +# EIP-4844 -- Polynomial Commitments + +## Table of contents + + + + + +- [Introduction](#introduction) +- [Custom types](#custom-types) +- [Constants](#constants) +- [Preset](#preset) + - [Trusted setup](#trusted-setup) +- [Helper functions](#helper-functions) + - [BLS12-381 helpers](#bls12-381-helpers) + - [`bls_modular_inverse`](#bls_modular_inverse) + - [`div`](#div) + - [`lincomb`](#lincomb) + - [KZG](#kzg) + - [`blob_to_kzg`](#blob_to_kzg) + - [`verify_kzg_proof`](#verify_kzg_proof) + - [Polynomials](#polynomials) + - [`evaluate_polynomial_in_evaluation_form`](#evaluate_polynomial_in_evaluation_form) + + + + + +## Introduction + +This document specifies basic polynomial operations and KZG polynomial commitment operations as they are needed for the EIP-4844 specification. The implementations are not optimized for performance, but readability. All practical implementations should optimize the polynomial operations. + +## Custom types + +| Name | SSZ equivalent | Description | +| - | - | - | +| `BLSFieldElement` | `uint256` | `x < BLS_MODULUS` | +| `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` | + +## Constants + +| Name | Value | Notes | +| - | - | - | +| `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` | Scalar field modulus of BLS12-381 | +| `ROOTS_OF_UNITY` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | Roots of unity of order FIELD_ELEMENTS_PER_BLOB over the BLS12-381 field | + +## Preset + +### Trusted setup + +The trusted setup is part of the preset: during testing a `minimal` insecure variant may be used, +but reusing the `mainnet` settings in public networks is a critical security requirement. + +| Name | Value | +| - | - | +| `KZG_SETUP_G2` | `Vector[G2Point, FIELD_ELEMENTS_PER_BLOB]`, contents TBD | +| `KZG_SETUP_LAGRANGE` | `Vector[KZGCommitment, FIELD_ELEMENTS_PER_BLOB]`, contents TBD | + +## Helper functions + +### BLS12-381 helpers + +#### `bls_modular_inverse` + +```python +def bls_modular_inverse(x: BLSFieldElement) -> BLSFieldElement: + """ + Compute the modular inverse of x using the eGCD algorithm + i.e. return y such that x * y % BLS_MODULUS == 1 and return 0 for x == 0 + """ + if x == 0: + return 0 + + lm, hm = 1, 0 + low, high = x % BLS_MODULUS, BLS_MODULUS + while low > 1: + r = high // low + nm, new = hm - lm * r, high - low * r + lm, low, hm, high = nm, new, lm, low + return lm % BLS_MODULUS +``` + +#### `div` + +```python +def div(x, y): + """Divide two field elements: `x` by `y`""" + return x * inv(y) % BLS_MODULUS +``` + +#### `lincomb` + +```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 +``` + +### KZG + +KZG core functions. These are also defined in EIP-4844 execution specs. + +#### `blob_to_kzg` + +```python +def blob_to_kzg(blob: Blob) -> KZGCommitment: + return lincomb(KZG_SETUP_LAGRANGE, blob) +``` + +#### `verify_kzg_proof` + +```python +def verify_kzg_proof(polynomial_kzg: KZGCommitment, + x: BLSFieldElement, + y: BLSFieldElement, + quotient_kzg: KZGProof) -> bool: + """Verify KZG proof that `p(x) == y` where `p(x)` is the polynomial represented by `polynomial_kzg`""" + # 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] + ]) +``` + +### Polynomials + +#### `evaluate_polynomial_in_evaluation_form` + +```python +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 +``` diff --git a/specs/eip4844/validator.md b/specs/eip4844/validator.md index c3dd83a41..d636b39e4 100644 --- a/specs/eip4844/validator.md +++ b/specs/eip4844/validator.md @@ -82,59 +82,6 @@ def vector_lincomb(vectors: List[List[BLSFieldElement]], scalars: List[BLSFieldE return [BLSFieldElement(x) for x in r] -def bls_modular_inverse(x: BLSFieldElement) -> BLSFieldElement: - """ - Compute the modular inverse of x using the eGCD algorithm - i.e. return y such that x * y % BLS_MODULUS == 1 and return 0 for x == 0 - """ - if x == 0: - return 0 - - lm, hm = 1, 0 - low, high = x % BLS_MODULUS, BLS_MODULUS - while low > 1: - r = high // low - nm, new = hm - lm * r, high - low * r - lm, low, hm, high = nm, new, lm, low - return lm % BLS_MODULUS - - -def div(x, y): - """Divide two field elements: `x` by `y`""" - return x * inv(y) % MODULUS - - -def verify_kzg_proof(polynomial_kzg: KZGCommitment, - x: BLSFieldElement, - y: BLSFieldElement, - quotient_kzg: KZGProof) -> bool: - """Verify KZG proof that `p(x) == y` where `p(x)` is the polynomial represented by `polynomial_kzg`""" - # 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] - ]) - - -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 - - def verify_blobs_sidecar(slot: Slot, beacon_block_root: Root, expected_kzgs: Sequence[KZGCommitment], blobs_sidecar: BlobsSidecar): assert slot == blobs_sidecar.beacon_block_slot