diff --git a/setup.py b/setup.py index 6db4aa8707..290f4852d0 100644 --- a/setup.py +++ b/setup.py @@ -232,7 +232,7 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str]) -> if not _is_constant_id(name): # Check for short type declarations - if value.startswith(("uint", "Bytes", "ByteList", "Union", "Vector", "List")): + if value.startswith(("uint", "Bytes", "ByteList", "Union", "Vector", "List", "ByteVector")): custom_types[name] = value continue @@ -590,7 +590,6 @@ def imports(cls, preset_name: str): return super().imports(preset_name) + f''' from eth2spec.utils import kzg from eth2spec.bellatrix import {preset_name} as bellatrix -from eth2spec.utils.ssz.ssz_impl import serialize as ssz_serialize ''' @@ -617,12 +616,13 @@ def sundry_functions(cls) -> str: ROOTS_OF_UNITY = kzg.compute_roots_of_unity(TESTING_FIELD_ELEMENTS_PER_BLOB) -def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> BlobsSidecar: - pass''' +def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> Optional[BlobsSidecar]: + return "TEST"''' @classmethod def hardcoded_custom_type_dep_constants(cls, spec_object) -> str: constants = { + 'BYTES_PER_FIELD_ELEMENT': spec_object.constant_vars['BYTES_PER_FIELD_ELEMENT'].value, 'FIELD_ELEMENTS_PER_BLOB': spec_object.preset_vars['FIELD_ELEMENTS_PER_BLOB'].value, 'MAX_BLOBS_PER_BLOCK': spec_object.preset_vars['MAX_BLOBS_PER_BLOCK'].value, } diff --git a/specs/eip4844/beacon-chain.md b/specs/eip4844/beacon-chain.md index 4cf9535933..a5f9124724 100644 --- a/specs/eip4844/beacon-chain.md +++ b/specs/eip4844/beacon-chain.md @@ -23,6 +23,8 @@ - [`ExecutionPayloadHeader`](#executionpayloadheader) - [Helper functions](#helper-functions) - [Misc](#misc) + - [`validate_blobs_sidecar`](#validate_blobs_sidecar) + - [`is_data_available`](#is_data_available) - [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash) - [`tx_peek_blob_versioned_hashes`](#tx_peek_blob_versioned_hashes) - [`verify_kzg_commitments_against_transactions`](#verify_kzg_commitments_against_transactions) @@ -44,9 +46,7 @@ This upgrade adds blobs to the beacon chain as part of EIP-4844. | Name | SSZ equivalent | Description | | - | - | - | -| `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 | ## Constants @@ -55,7 +55,6 @@ This upgrade adds blobs to the beacon chain as part of EIP-4844. | Name | Value | | - | - | | `BLOB_TX_TYPE` | `uint8(0x05)` | -| `FIELD_ELEMENTS_PER_BLOB` | `uint64(4096)` | | `VERSIONED_HASH_VERSION_KZG` | `Bytes1(0x01)` | ### Domain types @@ -150,6 +149,43 @@ class ExecutionPayloadHeader(Container): ### Misc +#### `validate_blobs_sidecar` + +```python +def validate_blobs_sidecar(slot: Slot, + beacon_block_root: Root, + expected_kzg_commitments: Sequence[KZGCommitment], + blobs_sidecar: BlobsSidecar) -> None: + assert slot == blobs_sidecar.beacon_block_slot + assert beacon_block_root == blobs_sidecar.beacon_block_root + blobs = blobs_sidecar.blobs + kzg_aggregated_proof = blobs_sidecar.kzg_aggregated_proof + assert len(expected_kzg_commitments) == len(blobs) + + assert verify_aggregate_kzg_proof(blobs, expected_kzg_commitments, kzg_aggregated_proof) +``` + +#### `is_data_available` + +The implementation of `is_data_available` is meant to change with later sharding upgrades. +Initially, it requires every verifying actor to retrieve the matching `BlobsSidecar`, +and validate the sidecar with `validate_blobs_sidecar`. + +Without the sidecar the block may be processed further optimistically, +but MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. + +```python +def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool: + # `retrieve_blobs_sidecar` is implementation dependent, raises an exception if not available. + sidecar = retrieve_blobs_sidecar(slot, beacon_block_root) + if sidecar == "TEST": + return True # For testing; remove once we have a way to inject `BlobsSidecar` into tests + validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar) + + return True +``` + + #### `kzg_commitment_to_versioned_hash` ```python @@ -204,6 +240,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_operations(state, block.body) process_sync_aggregate(state, block.body.sync_aggregate) process_blob_kzg_commitments(state, block.body) # [New in EIP-4844] + + # New in EIP-4844, note: Can sync optimistically without this condition, see note on `is_data_available` + assert is_data_available(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments) ``` #### Execution payload diff --git a/specs/eip4844/polynomial-commitments.md b/specs/eip4844/polynomial-commitments.md index 2b25763466..1ecfe4f369 100644 --- a/specs/eip4844/polynomial-commitments.md +++ b/specs/eip4844/polynomial-commitments.md @@ -10,6 +10,8 @@ - [Custom types](#custom-types) - [Constants](#constants) - [Preset](#preset) + - [Blob](#blob) + - [Crypto](#crypto) - [Trusted setup](#trusted-setup) - [Helper functions](#helper-functions) - [Bit-reversal permutation](#bit-reversal-permutation) @@ -18,16 +20,22 @@ - [`bit_reversal_permutation`](#bit_reversal_permutation) - [BLS12-381 helpers](#bls12-381-helpers) - [`bytes_to_bls_field`](#bytes_to_bls_field) + - [`blob_to_polynomial`](#blob_to_polynomial) + - [`hash_to_bls_field`](#hash_to_bls_field) - [`bls_modular_inverse`](#bls_modular_inverse) - [`div`](#div) - [`g1_lincomb`](#g1_lincomb) - - [`vector_lincomb`](#vector_lincomb) + - [`poly_lincomb`](#poly_lincomb) + - [`compute_powers`](#compute_powers) + - [Polynomials](#polynomials) + - [`evaluate_polynomial_in_evaluation_form`](#evaluate_polynomial_in_evaluation_form) - [KZG](#kzg) - [`blob_to_kzg_commitment`](#blob_to_kzg_commitment) - [`verify_kzg_proof`](#verify_kzg_proof) - [`compute_kzg_proof`](#compute_kzg_proof) - - [Polynomials](#polynomials) - - [`evaluate_polynomial_in_evaluation_form`](#evaluate_polynomial_in_evaluation_form) + - [`compute_aggregated_poly_and_commitment`](#compute_aggregated_poly_and_commitment) + - [`compute_aggregate_kzg_proof`](#compute_aggregate_kzg_proof) + - [`verify_aggregate_kzg_proof`](#verify_aggregate_kzg_proof) @@ -46,16 +54,31 @@ This document specifies basic polynomial operations and KZG polynomial commitmen | `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` | +| `Polynomial` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | a polynomial in evaluation form | +| `Blob` | `ByteVector[BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB]` | a basic blob data | ## 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 | +| `BYTES_PER_FIELD_ELEMENT` | `uint64(32)` | Bytes used to encode a BLS scalar field element | ## Preset +### Blob + +| Name | Value | +| - | - | +| `FIELD_ELEMENTS_PER_BLOB` | `uint64(4096)` | +| `FIAT_SHAMIR_PROTOCOL_DOMAIN` | `b'FSBLOBVERIFY_V1_'` | + +### Crypto + +| Name | Value | Notes | +| - | - | - | +| `ROOTS_OF_UNITY` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | Roots of unity of order FIELD_ELEMENTS_PER_BLOB over the BLS12-381 field | + ### Trusted setup The trusted setup is part of the preset: during testing a `minimal` insecure variant may be used, @@ -91,7 +114,7 @@ def is_power_of_two(value: int) -> bool: ```python def reverse_bits(n: int, order: int) -> int: """ - Reverse the bit order of an integer n + Reverse the bit order of an integer ``n``. """ assert is_power_of_two(order) # Convert n to binary with the same number of bits as "order" - 1, then reverse its bit order @@ -117,9 +140,51 @@ def bit_reversal_permutation(sequence: Sequence[T]) -> Sequence[T]: ```python def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement: """ - Convert bytes to a BLS field scalar. The output is not uniform over the BLS field. + Convert 32-byte value to a BLS field scalar. The output is not uniform over the BLS field. + """ + return int.from_bytes(b, ENDIANNESS) % BLS_MODULUS +``` + +#### `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): + value = int.from_bytes(blob[i * BYTES_PER_FIELD_ELEMENT: (i + 1) * BYTES_PER_FIELD_ELEMENT], ENDIANNESS) + assert value < BLS_MODULUS + polynomial[i] = value + return polynomial +``` + +#### `hash_to_bls_field` + +```python +def hash_to_bls_field(polys: Sequence[Polynomial], + comms: Sequence[KZGCommitment]) -> BLSFieldElement: + """ + Compute 32-byte hash of serialized polynomials and commitments concatenated. + This hash is then converted to a BLS field element, where the result is not uniform over the BLS field. + Return the BLS field element. """ - return int.from_bytes(b, "little") % BLS_MODULUS + # Append the number of polynomials and the degree of each polynomial as a domain separator + num_polys = int.to_bytes(len(polys), 8, ENDIANNESS) + degree_poly = int.to_bytes(FIELD_ELEMENTS_PER_BLOB, 8, ENDIANNESS) + data = FIAT_SHAMIR_PROTOCOL_DOMAIN + degree_poly + num_polys + + # Append each polynomial which is composed by field elements + for poly in polys: + for field_element in poly: + data += int.to_bytes(field_element, BYTES_PER_FIELD_ELEMENT, ENDIANNESS) + + # Append serialized G1 points + for commitment in comms: + data += commitment + + return bytes_to_bls_field(hash(data)) ``` #### `bls_modular_inverse` @@ -137,7 +202,9 @@ def bls_modular_inverse(x: BLSFieldElement) -> BLSFieldElement: ```python def div(x: BLSFieldElement, y: BLSFieldElement) -> BLSFieldElement: - """Divide two field elements: `x` by `y`""" + """ + Divide two field elements: ``x`` by `y``. + """ return (int(x) * int(bls_modular_inverse(y))) % BLS_MODULUS ``` @@ -155,22 +222,65 @@ def g1_lincomb(points: Sequence[KZGCommitment], scalars: Sequence[BLSFieldElemen return KZGCommitment(bls.G1_to_bytes48(result)) ``` -#### `vector_lincomb` +#### `poly_lincomb` ```python -def vector_lincomb(vectors: Sequence[Sequence[BLSFieldElement]], - scalars: Sequence[BLSFieldElement]) -> Sequence[BLSFieldElement]: +def poly_lincomb(polys: Sequence[Polynomial], + scalars: Sequence[BLSFieldElement]) -> Polynomial: """ - Given a list of ``vectors``, interpret it as a 2D matrix and compute the linear combination - of each column with `scalars`: return the resulting vector. + Given a list of ``polynomials``, interpret it as a 2D matrix and compute the linear combination + of each column with `scalars`: return the resulting polynomials. """ - result = [0] * len(vectors[0]) - for v, s in zip(vectors, scalars): + result = [0] * len(polys[0]) + for v, s in zip(polys, scalars): for i, x in enumerate(v): result[i] = (result[i] + int(s) * int(x)) % BLS_MODULUS return [BLSFieldElement(x) for x in result] ``` +#### `compute_powers` + +```python +def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]: + """ + Return ``x`` to power of [0, n-1]. + """ + current_power = 1 + powers = [] + for _ in range(n): + powers.append(BLSFieldElement(current_power)) + current_power = current_power * int(x) % BLS_MODULUS + return powers +``` + +### Polynomials + +#### `evaluate_polynomial_in_evaluation_form` + +```python +def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial, + z: BLSFieldElement) -> BLSFieldElement: + """ + Evaluate a polynomial (in evaluation form) at an arbitrary point ``z``. + Uses the barycentric formula: + 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 + inverse_width = bls_modular_inverse(width) + + # Make sure we won't divide by zero during division + assert z not in ROOTS_OF_UNITY + + roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY) + + result = 0 + for i in range(width): + result += div(int(polynomial[i]) * int(roots_of_unity_brp[i]), (int(z) - roots_of_unity_brp[i])) + result = result * (pow(z, width, BLS_MODULUS) - 1) * inverse_width % BLS_MODULUS + return result +``` + ### KZG KZG core functions. These are also defined in EIP-4844 execution specs. @@ -179,7 +289,7 @@ KZG core functions. These are also defined in EIP-4844 execution specs. ```python def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment: - return g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob) + return g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob_to_polynomial(blob)) ``` #### `verify_kzg_proof` @@ -204,16 +314,16 @@ def verify_kzg_proof(polynomial_kzg: KZGCommitment, #### `compute_kzg_proof` ```python -def compute_kzg_proof(polynomial: Sequence[BLSFieldElement], z: BLSFieldElement) -> KZGProof: +def compute_kzg_proof(polynomial: Polynomial, z: BLSFieldElement) -> KZGProof: """ Compute KZG proof at point `z` with `polynomial` being in evaluation form + Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z) """ # To avoid SSZ overflow/underflow, convert element into int polynomial = [int(i) for i in polynomial] z = int(z) - # Shift our polynomial first (in evaluation form we can't handle the division remainder) y = evaluate_polynomial_in_evaluation_form(polynomial, z) polynomial_shifted = [(p - int(y)) % BLS_MODULUS for p in polynomial] @@ -226,31 +336,56 @@ def compute_kzg_proof(polynomial: Sequence[BLSFieldElement], z: BLSFieldElement) return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), quotient_polynomial)) ``` -### Polynomials - -#### `evaluate_polynomial_in_evaluation_form` +#### `compute_aggregated_poly_and_commitment` ```python -def evaluate_polynomial_in_evaluation_form(polynomial: Sequence[BLSFieldElement], - z: BLSFieldElement) -> BLSFieldElement: +def compute_aggregated_poly_and_commitment( + blobs: Sequence[Blob], + kzg_commitments: Sequence[KZGCommitment]) -> Tuple[Polynomial, KZGCommitment, BLSFieldElement]: """ - Evaluate a polynomial (in evaluation form) at an arbitrary point `z` - Uses the barycentric formula: - f(z) = (1 - z**WIDTH) / WIDTH * sum_(i=0)^WIDTH (f(DOMAIN[i]) * DOMAIN[i]) / (z - DOMAIN[i]) + Return (1) the aggregated polynomial, (2) the aggregated KZG commitment, + and (3) the polynomial evaluation random challenge. """ - width = len(polynomial) - assert width == FIELD_ELEMENTS_PER_BLOB - inverse_width = bls_modular_inverse(width) + # Generate random linear combination challenges + r = hash_to_bls_field(blobs, kzg_commitments) + r_powers = compute_powers(r, len(kzg_commitments)) + evaluation_challenge = int(r_powers[-1]) * r % BLS_MODULUS - # Make sure we won't divide by zero during division - assert z not in ROOTS_OF_UNITY + # Create aggregated polynomial in evaluation form + aggregated_poly = Polynomial(poly_lincomb([blob_to_polynomial(blob) for blob in blobs], r_powers)) - roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY) + # Compute commitment to aggregated polynomial + aggregated_poly_commitment = KZGCommitment(g1_lincomb(kzg_commitments, r_powers)) - result = 0 - for i in range(width): - result += div(int(polynomial[i]) * int(roots_of_unity_brp[i]), (z - roots_of_unity_brp[i])) - result = result * (pow(z, width, BLS_MODULUS) - 1) * inverse_width % BLS_MODULUS - return result + return aggregated_poly, aggregated_poly_commitment, evaluation_challenge +``` + +#### `compute_aggregate_kzg_proof` + +```python +def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof: + commitments = [blob_to_kzg_commitment(blob) for blob in blobs] + aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment( + blobs, + commitments + ) + return compute_kzg_proof(aggregated_poly, evaluation_challenge) ``` +#### `verify_aggregate_kzg_proof` + +```python +def verify_aggregate_kzg_proof(blobs: Sequence[Blob], + expected_kzg_commitments: Sequence[KZGCommitment], + kzg_aggregated_proof: KZGCommitment) -> bool: + aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment( + blobs, + expected_kzg_commitments, + ) + + # Evaluate aggregated polynomial at `evaluation_challenge` (evaluation function checks for div-by-zero) + y = evaluate_polynomial_in_evaluation_form(aggregated_poly, evaluation_challenge) + + # Verify aggregated proof + return verify_kzg_proof(aggregated_poly_commitment, evaluation_challenge, y, kzg_aggregated_proof) +``` diff --git a/specs/eip4844/validator.md b/specs/eip4844/validator.md index e1bcf9564a..f4223bd2f3 100644 --- a/specs/eip4844/validator.md +++ b/specs/eip4844/validator.md @@ -10,17 +10,7 @@ - [Introduction](#introduction) - [Prerequisites](#prerequisites) -- [Custom types](#custom-types) -- [Containers](#containers) - - [`BlobsAndCommitments`](#blobsandcommitments) - - [`PolynomialAndCommitment`](#polynomialandcommitment) - [Helpers](#helpers) - - [`is_data_available`](#is_data_available) - - [`hash_to_bls_field`](#hash_to_bls_field) - - [`compute_powers`](#compute_powers) - - [`compute_aggregated_poly_and_commitment`](#compute_aggregated_poly_and_commitment) - - [`validate_blobs_sidecar`](#validate_blobs_sidecar) - - [`compute_proof_from_blobs`](#compute_proof_from_blobs) - [`get_blobs_and_kzg_commitments`](#get_blobs_and_kzg_commitments) - [Beacon chain responsibilities](#beacon-chain-responsibilities) - [Block proposal](#block-proposal) @@ -43,140 +33,8 @@ All behaviors and definitions defined in this document, and documents it extends All terminology, constants, functions, and protocol mechanics defined in the updated [Beacon Chain doc of EIP4844](./beacon-chain.md) are requisite for this document and used throughout. Please see related Beacon Chain doc before continuing and use them as a reference throughout. -## Custom types - -| Name | SSZ equivalent | Description | -| - | - | - | -| `Polynomial` | `List[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | a polynomial in evaluation form | - -## Containers - -### `BlobsAndCommitments` - -```python -class BlobsAndCommitments(Container): - blobs: List[Blob, MAX_BLOBS_PER_BLOCK] - kzg_commitments: List[KZGCommitment, MAX_BLOBS_PER_BLOCK] -``` - -### `PolynomialAndCommitment` - -```python -class PolynomialAndCommitment(Container): - polynomial: Polynomial - kzg_commitment: KZGCommitment -``` - - ## Helpers -### `is_data_available` - -The implementation of `is_data_available` is meant to change with later sharding upgrades. -Initially, it requires every verifying actor to retrieve the matching `BlobsSidecar`, -and validate the sidecar with `validate_blobs_sidecar`. - -Without the sidecar the block may be processed further optimistically, -but MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. - -```python -def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool: - # `retrieve_blobs_sidecar` is implementation dependent, raises an exception if not available. - sidecar = retrieve_blobs_sidecar(slot, beacon_block_root) - validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar) - - return True -``` - -### `hash_to_bls_field` - -```python -def hash_to_bls_field(x: Container) -> BLSFieldElement: - """ - Compute 32-byte hash of serialized container and convert it to BLS field. - The output is not uniform over the BLS field. - """ - return bytes_to_bls_field(hash(ssz_serialize(x))) -``` - -### `compute_powers` -```python -def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]: - """ - Return ``x`` to power of [0, n-1]. - """ - current_power = 1 - powers = [] - for _ in range(n): - powers.append(BLSFieldElement(current_power)) - current_power = current_power * int(x) % BLS_MODULUS - return powers -``` - -### `compute_aggregated_poly_and_commitment` - -```python -def compute_aggregated_poly_and_commitment( - blobs: Sequence[Blob], - kzg_commitments: Sequence[KZGCommitment]) -> Tuple[Polynomial, KZGCommitment]: - """ - Return the aggregated polynomial and aggregated KZG commitment. - """ - # Generate random linear combination challenges - r = hash_to_bls_field(BlobsAndCommitments(blobs=blobs, kzg_commitments=kzg_commitments)) - r_powers = compute_powers(r, len(kzg_commitments)) - - # Create aggregated polynomial in evaluation form - aggregated_poly = Polynomial(vector_lincomb(blobs, r_powers)) - - # Compute commitment to aggregated polynomial - aggregated_poly_commitment = KZGCommitment(g1_lincomb(kzg_commitments, r_powers)) - - return aggregated_poly, aggregated_poly_commitment -``` - -### `validate_blobs_sidecar` - -```python -def validate_blobs_sidecar(slot: Slot, - beacon_block_root: Root, - expected_kzg_commitments: Sequence[KZGCommitment], - blobs_sidecar: BlobsSidecar) -> None: - assert slot == blobs_sidecar.beacon_block_slot - assert beacon_block_root == blobs_sidecar.beacon_block_root - blobs = blobs_sidecar.blobs - kzg_aggregated_proof = blobs_sidecar.kzg_aggregated_proof - assert len(expected_kzg_commitments) == len(blobs) - - aggregated_poly, aggregated_poly_commitment = compute_aggregated_poly_and_commitment( - blobs, - expected_kzg_commitments, - ) - - # Generate challenge `x` and evaluate the aggregated polynomial at `x` - 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, kzg_aggregated_proof) -``` - -### `compute_proof_from_blobs` - -```python -def compute_proof_from_blobs(blobs: Sequence[Blob]) -> KZGProof: - commitments = [blob_to_kzg_commitment(blob) for blob in blobs] - aggregated_poly, aggregated_poly_commitment = compute_aggregated_poly_and_commitment(blobs, commitments) - x = hash_to_bls_field(PolynomialAndCommitment( - polynomial=aggregated_poly, - kzg_commitment=aggregated_poly_commitment, - )) - return compute_kzg_proof(aggregated_poly, x) -``` - ### `get_blobs_and_kzg_commitments` The interface to retrieve blobs and corresponding kzg commitments. @@ -231,7 +89,7 @@ def get_blobs_sidecar(block: BeaconBlock, blobs: Sequence[Blob]) -> BlobsSidecar beacon_block_root=hash_tree_root(block), beacon_block_slot=block.slot, blobs=blobs, - kzg_aggregated_proof=compute_proof_from_blobs(blobs), + kzg_aggregated_proof=compute_aggregate_kzg_proof(blobs), ) ``` diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/__init__.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/test_polynomial_commitments.py new file mode 100644 index 0000000000..dea6aeb8c9 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -0,0 +1,20 @@ +from eth2spec.test.context import ( + spec_state_test, + with_eip4844_and_later, +) +from eth2spec.test.helpers.sharding import ( + get_sample_blob, +) + + +@with_eip4844_and_later +@spec_state_test +def test_verify_kzg_proof(spec, state): + x = 3 + blob = get_sample_blob(spec) + commitment = spec.blob_to_kzg_commitment(blob) + polynomial = spec.blob_to_polynomial(blob) + proof = spec.compute_kzg_proof(polynomial, x) + + y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x) + assert spec.verify_kzg_proof(commitment, x, y, proof) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py index d0250f3df7..ddb46ad9e3 100644 --- a/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py @@ -10,26 +10,10 @@ ) from eth2spec.test.helpers.sharding import ( get_sample_opaque_tx, - get_sample_blob, ) from eth2spec.test.helpers.keys import privkeys -@with_eip4844_and_later -@spec_state_test -def test_verify_kzg_proof(spec, state): - x = 3 - polynomial = get_sample_blob(spec) - polynomial = [int(i) for i in polynomial] - commitment = spec.blob_to_kzg_commitment(polynomial) - - # Get the proof - proof = spec.compute_kzg_proof(polynomial, x) - - y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x) - assert spec.verify_kzg_proof(commitment, x, y, proof) - - def _run_validate_blobs_sidecar_test(spec, state, blob_count): block = build_empty_block_for_next_slot(spec, state) opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) diff --git a/tests/core/pyspec/eth2spec/test/helpers/sharding.py b/tests/core/pyspec/eth2spec/test/helpers/sharding.py index 6c90153fca..3ce3215eb4 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/sharding.py +++ b/tests/core/pyspec/eth2spec/test/helpers/sharding.py @@ -53,10 +53,16 @@ def get_sample_blob(spec, rng=None): if rng is None: rng = random.Random(5566) - return spec.Blob([ + values = [ rng.randint(0, spec.BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB) - ]) + ] + + b = bytes() + for v in values: + b += v.to_bytes(32, spec.ENDIANNESS) + + return spec.Blob(b) def get_sample_opaque_tx(spec, blob_count=1, rng=None):