From 851d38053683ce922e61e2cd0c0d2f4474a03d5a Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Mon, 12 Sep 2022 10:48:42 +0300 Subject: [PATCH] Synchronize EIP-4844 cryptography with consensus specs 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. --- EIPS/eip-4844.md | 126 ++++++++--------------------------------------- 1 file changed, 20 insertions(+), 106 deletions(-) diff --git a/EIPS/eip-4844.md b/EIPS/eip-4844.md index 72e931012b59a..5a0059b9774a5 100644 --- a/EIPS/eip-4844.md +++ b/EIPS/eip-4844.md @@ -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` | @@ -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 @@ -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