diff --git a/da/common.py b/da/common.py index 521cd5f..d8ff0de 100644 --- a/da/common.py +++ b/da/common.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from itertools import chain from typing import List, Generator from eth2spec.eip7594.mainnet import Bytes32 @@ -16,7 +17,8 @@ class Column(List[Bytes32]): class Row(List[Bytes32]): - pass + def as_bytes(self) -> bytes: + return bytes(chain.from_iterable(self)) class ChunksMatrix(List[Row]): diff --git a/da/encoder.py b/da/encoder.py index 06cf999..c0628a7 100644 --- a/da/encoder.py +++ b/da/encoder.py @@ -3,7 +3,7 @@ from itertools import batched, chain from typing import List, Sequence, Tuple from eth2spec.eip7594.mainnet import KZGCommitment as Commitment, KZGProof as Proof, BLSFieldElement -from da.common import ChunksMatrix, Chunk +from da.common import ChunksMatrix, Chunk, Row from da.kzg_rs import kzg, rs, poly from da.kzg_rs.common import GLOBAL_PARAMETERS, ROOTS_OF_UNITY from da.kzg_rs.poly import Polynomial @@ -32,22 +32,23 @@ class DAEncoder: def _chunkify_data(self, data: bytes) -> ChunksMatrix: size: int = self.params.column_count * self.params.bytes_per_field_element - return ChunksMatrix(bytes(b) for b in batched(data, size)) + return ChunksMatrix( + Row([bytes(chunk) for chunk in batched(b, self.params.bytes_per_field_element)]) + for b in batched(data, size) + ) @staticmethod - def _compute_row_kzg_commitments(rows: Sequence[bytes]) -> List[Tuple[Polynomial, Commitment]]: - return [kzg.bytes_to_commitment(row, GLOBAL_PARAMETERS) for row in rows] + def _compute_row_kzg_commitments(rows: Sequence[Row]) -> List[Tuple[Polynomial, Commitment]]: + return [kzg.bytes_to_commitment(row.as_bytes(), GLOBAL_PARAMETERS) for row in rows] def _rs_encode_rows(self, chunks_matrix: ChunksMatrix) -> ChunksMatrix: - def __rs_encode_row(row: bytes) -> bytes: - polynomial = kzg.bytes_to_polynomial(row) - return bytes( - chain.from_iterable( - Chunk(BLSFieldElement.to_bytes( - x, - length=self.params.bytes_per_field_element, byteorder="big" - )) for x in rs.encode(polynomial, 2, ROOTS_OF_UNITY) - ) + def __rs_encode_row(row: Row) -> Row: + polynomial = kzg.bytes_to_polynomial(row.as_bytes()) + return Row( + Chunk(BLSFieldElement.to_bytes( + x, + length=self.params.bytes_per_field_element, byteorder="big" + )) for x in rs.encode(polynomial, 2, ROOTS_OF_UNITY) ) return ChunksMatrix(__rs_encode_row(row) for row in chunks_matrix) @@ -61,7 +62,7 @@ class DAEncoder: proofs.append( [ kzg.generate_element_proof(i, poly, GLOBAL_PARAMETERS, ROOTS_OF_UNITY) - for i in range(len(row)//self.params.bytes_per_field_element) + for i in range(len(row)) ] ) return proofs diff --git a/da/kzg_rs/kzg.py b/da/kzg_rs/kzg.py index d655a53..0c166ed 100644 --- a/da/kzg_rs/kzg.py +++ b/da/kzg_rs/kzg.py @@ -54,14 +54,14 @@ def generate_element_proof( def verify_element_proof( - polynomial: Polynomial, + chunk: BLSFieldElement, commitment: Commitment, proof: Proof, element_index: int, roots_of_unity: Sequence[BLSFieldElement], ) -> bool: u = int(roots_of_unity[element_index]) - v = polynomial.eval(u) + v = chunk commitment_check_G1 = bls.bytes48_to_G1(commitment) - bls.multiply(bls.G1(), v) proof_check_g2 = bls.add( GLOBAL_PARAMETERS_G2[1], diff --git a/da/kzg_rs/test_kzg.py b/da/kzg_rs/test_kzg.py index fcd9182..d9e46e2 100644 --- a/da/kzg_rs/test_kzg.py +++ b/da/kzg_rs/test_kzg.py @@ -2,7 +2,7 @@ from itertools import chain, batched from random import randrange from unittest import TestCase -from eth2spec.deneb.mainnet import BLS_MODULUS, bytes_to_bls_field +from eth2spec.deneb.mainnet import BLS_MODULUS, bytes_to_bls_field, BLSFieldElement from da.kzg_rs import kzg from da.kzg_rs.common import BYTES_PER_FIELD_ELEMENT, GLOBAL_PARAMETERS, ROOTS_OF_UNITY, GLOBAL_PARAMETERS_G2 @@ -12,11 +12,11 @@ from da.kzg_rs.trusted_setup import verify_setup class TestKZG(TestCase): @staticmethod - def rand_bytes(size=1024): - return bytearray( + def rand_bytes(n_chunks=1024): + return bytes( chain.from_iterable( int.to_bytes(randrange(BLS_MODULUS), length=BYTES_PER_FIELD_ELEMENT) - for _ in range(size) + for _ in range(n_chunks) ) ) @@ -24,11 +24,15 @@ class TestKZG(TestCase): self.assertTrue(verify_setup((GLOBAL_PARAMETERS, GLOBAL_PARAMETERS_G2))) def test_poly_forms(self): - rand_bytes = self.rand_bytes(8) + n_chunks = 16 + rand_bytes = self.rand_bytes(n_chunks) eval_form = [int(bytes_to_bls_field(b)) for b in batched(rand_bytes, int(BYTES_PER_FIELD_ELEMENT))] poly = kzg.bytes_to_polynomial(rand_bytes) self.assertEqual(poly.evaluation_form(), eval_form) - self.assertEqual(poly.evaluation_form()[0], poly.eval(int(ROOTS_OF_UNITY[0]))) + for i, chunk in enumerate(eval_form): + self.assertEqual(poly.eval(ROOTS_OF_UNITY[i]), chunk) + for i in range(n_chunks): + self.assertEqual(poly.evaluation_form()[i], poly.eval(int(ROOTS_OF_UNITY[i]))) def test_commitment(self): rand_bytes = self.rand_bytes(32) @@ -46,16 +50,18 @@ class TestKZG(TestCase): rand_bytes = self.rand_bytes(n_chunks) _, commit = kzg.bytes_to_commitment(rand_bytes, GLOBAL_PARAMETERS) poly = kzg.bytes_to_polynomial(rand_bytes) - for n in range(n_chunks): - proof = kzg.generate_element_proof(n, poly, GLOBAL_PARAMETERS, ROOTS_OF_UNITY) + for i, chunk in enumerate(batched(rand_bytes, BYTES_PER_FIELD_ELEMENT)): + chunk = bytes(chunk) + proof = kzg.generate_element_proof(i, poly, GLOBAL_PARAMETERS, ROOTS_OF_UNITY) self.assertEqual(len(proof), 48) + self.assertEqual(poly.eval(int(ROOTS_OF_UNITY[i])), bytes_to_bls_field(chunk)) self.assertTrue(kzg.verify_element_proof( - poly, commit, proof, n, ROOTS_OF_UNITY + bytes_to_bls_field(chunk), commit, proof, i, ROOTS_OF_UNITY ) ) proof = kzg.generate_element_proof(0, poly, GLOBAL_PARAMETERS, ROOTS_OF_UNITY) for n in range(1, n_chunks): self.assertFalse(kzg.verify_element_proof( - poly, commit, proof, n, ROOTS_OF_UNITY + BLSFieldElement(0), commit, proof, n, ROOTS_OF_UNITY ) ) \ No newline at end of file diff --git a/da/test_encoder.py b/da/test_encoder.py index f1177ec..a4e9eb8 100644 --- a/da/test_encoder.py +++ b/da/test_encoder.py @@ -37,8 +37,9 @@ class TestEncoder(TestCase): _encoder = encoder.DAEncoder(encoder_settings) chunks_matrix = _encoder._chunkify_data(data) self.assertEqual(len(chunks_matrix), elements//encoder_settings.column_count) - for column in chunks_matrix: - self.assertEqual(len(column), encoder_settings.bytes_per_field_element*encoder_settings.column_count) + for row in chunks_matrix: + self.assertEqual(len(row), encoder_settings.column_count) + self.assertEqual(len(row[0]), encoder_settings.bytes_per_field_element) def test_compute_row_kzg_commitments(self): chunks_matrix = self.encoder._chunkify_data(self.data) @@ -50,8 +51,8 @@ class TestEncoder(TestCase): extended_chunks_matrix = self.encoder._rs_encode_rows(chunks_matrix) for r1, r2 in zip(chunks_matrix, extended_chunks_matrix): self.assertEqual(len(r1), len(r2)//2) - r2 = [BLSFieldElement.from_bytes(x) for x in batched(r2, self.params.bytes_per_field_element)] - poly_1 = kzg.bytes_to_polynomial(r1) + r2 = [BLSFieldElement.from_bytes(x) for x in r2] + poly_1 = kzg.bytes_to_polynomial(r1.as_bytes()) # we check against decoding so we now the encoding was properly done poly_2 = rs.decode(r2, ROOTS_OF_UNITY, len(poly_1)) self.assertEqual(poly_1, poly_2) @@ -64,12 +65,13 @@ class TestEncoder(TestCase): extended_proofs = self.encoder._compute_rows_proofs(extended_chunks_matrix, polynomials, commitments) # check original sized matrix for row, poly, commitment, proofs in zip(chunks_matrix, polynomials, commitments, original_proofs): - for i in range(len(proofs)): - self.assertTrue(kzg.verify_element_proof(poly, commitment, proofs[i], i, ROOTS_OF_UNITY)) + self.assertEqual(len(proofs), len(row)) + for i, chunk in enumerate(row): + self.assertTrue(kzg.verify_element_proof(BLSFieldElement.from_bytes(chunk), commitment, proofs[i], i, ROOTS_OF_UNITY)) # check extended matrix for row, poly, commitment, proofs in zip(extended_chunks_matrix, polynomials, commitments, extended_proofs): - for i in range(len(proofs)): - self.assertTrue(kzg.verify_element_proof(poly, commitment, proofs[i], i, ROOTS_OF_UNITY)) + for i, chunk in enumerate(row): + self.assertTrue(kzg.verify_element_proof(BLSFieldElement.from_bytes(chunk), commitment, proofs[i], i, ROOTS_OF_UNITY)) def test_compute_column_kzg_commitments(self): pass