From 6e026f3a7e14757e1cb71da1a98effd36f0c3712 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 4 Jun 2025 15:14:04 +0200 Subject: [PATCH] Refactor code and tests --- da/common.py | 18 ------------ da/encoder.py | 18 ++---------- da/kzg_rs/bdfg_proving.py | 61 +++++++++++++++++++++++++++++++++++++++ da/test_encoder.py | 46 ++++++++++------------------- da/verifier.py | 36 +++++++++-------------- 5 files changed, 94 insertions(+), 85 deletions(-) create mode 100644 da/kzg_rs/bdfg_proving.py diff --git a/da/common.py b/da/common.py index 4194ae0..e40605d 100644 --- a/da/common.py +++ b/da/common.py @@ -4,10 +4,8 @@ from itertools import chain, zip_longest, compress from typing import List, Generator, Self, Sequence from eth2spec.eip7594.mainnet import Bytes32, KZGCommitment as Commitment -from eth2spec.eip7594.mainnet import BLSFieldElement from py_ecc.bls import G2ProofOfPossession - type BlobId = bytes class NodeId(Bytes32): @@ -49,22 +47,6 @@ def build_blob_id(row_commitments: Sequence[Commitment]) -> BlobId: return hasher.digest() -def derive_challenge(row_commitments: List[Commitment]) -> BLSFieldElement: - """ - Derive a Fiat–Shamir challenge scalar h from the row commitments: - h = BLAKE2b-31( DST || bytes(com1) || bytes(com2) || ... ) - """ - _DST = b"NOMOS_DA_V1" - h = blake2b(digest_size=31) - h.update(_DST) - for com in row_commitments: - h.update(bytes(com)) - digest31 = h.digest() # 31 bytes - # pad to 32 bytes for field element conversion - padded = digest31 + b'\x00' - return BLSFieldElement.from_bytes(padded) - - class NomosDaG2ProofOfPossession(G2ProofOfPossession): # Domain specific tag for Nomos DA protocol DST = b"NOMOS_DA_AVAIL" diff --git a/da/encoder.py b/da/encoder.py index 8e57aab..2c63c49 100644 --- a/da/encoder.py +++ b/da/encoder.py @@ -1,7 +1,6 @@ from dataclasses import dataclass from itertools import batched -from typing import List, Sequence, Tuple -from hashlib import blake2b +from typing import List, Tuple from eth2spec.eip7594.mainnet import KZGCommitment as Commitment, KZGProof as Proof, BLSFieldElement @@ -9,6 +8,7 @@ from da.common import ChunksMatrix, Chunk, Row, derive_challenge from da.kzg_rs import kzg, rs from da.kzg_rs.common import GLOBAL_PARAMETERS, ROOTS_OF_UNITY, BYTES_PER_FIELD_ELEMENT, BLS_MODULUS from da.kzg_rs.poly import Polynomial +from da.kzg_rs.bdfg_proving import compute_combined_polynomial # Domain separation tag _DST = b"NOMOS_DA_V1" @@ -64,18 +64,6 @@ class DAEncoder: ) return ChunksMatrix(__rs_encode_row(row) for row in chunks_matrix) - @staticmethod - def _combined_polynomial( - polys: Sequence[Polynomial], h: BLSFieldElement - ) -> Polynomial: - combined_polynomial = polys[0] - h_int = int(h) # raw integer challenge - int_pow = 1 - for poly in polys[1:]: - int_pow = (int_pow * h_int) % BLS_MODULUS - combined_polynomial = combined_polynomial + Polynomial({int_pow * coeff for coeff in poly},BLS_MODULUS) - return combined_polynomial - def _compute_combined_column_proofs(self, combined_poly: Polynomial) -> List[Proof]: total_cols = self.params.column_count * 2 return [ @@ -88,7 +76,7 @@ class DAEncoder: row_polynomials, row_commitments = zip(*self._compute_row_kzg_commitments(chunks_matrix)) extended_matrix = self._rs_encode_rows(chunks_matrix) h = derive_challenge(row_commitments) - combined_poly = self._combined_polynomial(row_polynomials, h) + combined_poly = compute_combined_polynomial(row_polynomials, h) combined_column_proofs = self._compute_combined_column_proofs(combined_poly) result = EncodedData( data, diff --git a/da/kzg_rs/bdfg_proving.py b/da/kzg_rs/bdfg_proving.py new file mode 100644 index 0000000..d2a85e5 --- /dev/null +++ b/da/kzg_rs/bdfg_proving.py @@ -0,0 +1,61 @@ +from hashlib import blake2b +from typing import List, Sequence + +from da.common import Chunk +from da.kzg_rs.common import BLS_MODULUS + +from eth2spec.eip7594.mainnet import BLSFieldElement, KZGCommitment as Commitment +from eth2spec.utils import bls + +from da.kzg_rs.poly import Polynomial + + +def derive_challenge(row_commitments: List[Commitment]) -> BLSFieldElement: + """ + Derive a Fiat–Shamir challenge scalar h from the row commitments: + h = BLAKE2b-31( DST || bytes(com1) || bytes(com2) || ... ) + """ + _DST = b"NOMOS_DA_V1" + h = blake2b(digest_size=31) + h.update(_DST) + for com in row_commitments: + h.update(bytes(com)) + digest31 = h.digest() # 31 bytes + # pad to 32 bytes for field element conversion + padded = digest31 + b'\x00' + return BLSFieldElement.from_bytes(padded) + + +def combine_commitments(row_commitments: List[Commitment], h: BLSFieldElement) -> Commitment: + combined_commitment = bls.bytes48_to_G1(row_commitments[0]) + power = int(h) % BLS_MODULUS + for commitment in row_commitments[1:]: + commitment = bls.bytes48_to_G1(commitment) + combined_commitment = bls.add(combined_commitment, bls.multiply(commitment, power)) + power = (power * int(h)) % BLS_MODULUS + return bls.G1_to_bytes48(combined_commitment) + + +def compute_combined_polynomial( + polys: Sequence[Polynomial], h: BLSFieldElement + ) -> Polynomial: + combined_polynomial = polys[0] + h_int = int(h) # raw integer challenge + int_pow = 1 + for poly in polys[1:]: + int_pow = (int_pow * h_int) % BLS_MODULUS + combined_polynomial = combined_polynomial + Polynomial([int_pow * coeff for coeff in poly], BLS_MODULUS) + return combined_polynomial + +def compute_combined_evaluation( + evals: Sequence[Chunk], + h: BLSFieldElement +) -> BLSFieldElement: + combined_eval_int = 0 + power_int = 1 + h_int = int(h) % BLS_MODULUS + for chunk in evals: + chunk_int = int.from_bytes(bytes(chunk), byteorder="big") + combined_eval_int = (combined_eval_int + chunk_int * power_int) % BLS_MODULUS + power_int = (power_int * h_int) % BLS_MODULUS + return BLSFieldElement(combined_eval_int) \ No newline at end of file diff --git a/da/test_encoder.py b/da/test_encoder.py index 56eac1a..8f834a8 100644 --- a/da/test_encoder.py +++ b/da/test_encoder.py @@ -1,16 +1,15 @@ from itertools import chain, batched -from random import randrange, randbytes +from random import randbytes from unittest import TestCase -from eth2spec.utils import bls -from eth2spec.deneb.mainnet import bytes_to_bls_field from da import encoder -from da.common import derive_challenge +from da.common import Column +from kzg_rs.bdfg_proving import derive_challenge from da.encoder import DAEncoderParams, DAEncoder -from da.verifier import DAVerifier +from da.verifier import DAVerifier, DAShare from eth2spec.eip7594.mainnet import BYTES_PER_FIELD_ELEMENT, BLSFieldElement -from da.kzg_rs.common import BLS_MODULUS, ROOTS_OF_UNITY +from da.kzg_rs.common import ROOTS_OF_UNITY from da.kzg_rs import kzg, rs @@ -35,32 +34,19 @@ class TestEncoder(TestCase): self.assertEqual(columns_len, column_count) chunks_size = (len(data) // encoder_params.bytes_per_chunk) // encoder_params.column_count self.assertEqual(len(encoded_data.row_commitments), chunks_size) - self.assertEqual(len(encoded_data.combined_column_proofs), columns_len) - # verify rows - h = derive_challenge(encoded_data.row_commitments) - combined_commitment = bls.bytes48_to_G1(encoded_data.row_commitments[0]) - power = int(h) % BLS_MODULUS - for commitment in encoded_data.row_commitments[1:]: - commitment=bls.bytes48_to_G1(commitment) - combined_commitment = bls.add(combined_commitment,bls.multiply(commitment,power)) - power = (power * int(h)) % BLS_MODULUS - combined_commitment = bls.G1_to_bytes48(combined_commitment) - for i, (column, proof) in enumerate(zip(encoded_data.extended_matrix.columns, encoded_data.combined_column_proofs)): - combined_eval_int = 0 - power_int = 1 - h_int = int(h) - for data in column: - chunk_int = int.from_bytes(bytes(data), byteorder="big") - combined_eval_point = (combined_eval_int + chunk_int * power_int) % BLS_MODULUS - power_int = (power_int * h_int) % BLS_MODULUS - kzg.verify_element_proof( - combined_eval_point, - combined_commitment, - proof, - i, - ROOTS_OF_UNITY + verifier = DAVerifier() + # verify columns + for idx, (column, column_proof) in enumerate(zip(encoded_data.extended_matrix.columns, encoded_data.combined_column_proofs)): + share = DAShare( + column=Column(column), + column_idx=idx, + combined_column_proof=column_proof, + row_commitments=encoded_data.row_commitments ) + verifier.verify(share) + + def test_chunkify(self): encoder_settings = DAEncoderParams(column_count=2, bytes_per_chunk=31) diff --git a/da/verifier.py b/da/verifier.py index 42d7ce6..d492535 100644 --- a/da/verifier.py +++ b/da/verifier.py @@ -1,16 +1,15 @@ from dataclasses import dataclass -from typing import List, Sequence, Set -from eth2spec.utils import bls -from eth2spec.deneb.mainnet import BLSFieldElement +from typing import List + from eth2spec.eip7594.mainnet import ( KZGCommitment as Commitment, KZGProof as Proof, ) -import da.common -from da.common import Column, Chunk, BlobId, build_blob_id, derive_challenge +from da.common import Column, BlobId, build_blob_id from da.kzg_rs import kzg -from da.kzg_rs.common import ROOTS_OF_UNITY, GLOBAL_PARAMETERS, BLS_MODULUS +from da.kzg_rs.bdfg_proving import combine_commitments, derive_challenge, compute_combined_evaluation +from da.kzg_rs.common import ROOTS_OF_UNITY # Domain separation tag _DST = b"NOMOS_DA_V1" @@ -37,21 +36,14 @@ class DAVerifier: # 1. Derive challenge h = derive_challenge(blob.row_commitments) # 2. Reconstruct combined commitment: combined_commitment = sum_{i=0..l-1} h^i * row_commitments[i] - combined_commitment = bls.bytes48_to_G1(blob.row_commitments[0]) - power = int(h) % BLS_MODULUS - for commitment in blob.row_commitments[1:]: - commitment = bls.bytes48_to_G1(commitment) - combined_commitment = bls.add(combined_commitment,bls.multiply(commitment, power)) - power = (power * int(h)) % BLS_MODULUS - combined_commitment = bls.G1_to_bytes48(combined_commitment) + combined_commitment = combine_commitments(blob.row_commitments, h) # 3. Compute combined evaluation v = sum_{i=0..l-1} (h^i * column_data[i]) - combined_eval_int = 0 - power_int = 1 - h_int = int(h) % BLS_MODULUS - for chunk in blob.column: - chunk_int = int.from_bytes(bytes(chunk), byteorder="big") - combined_eval_int = (combined_eval_int + chunk_int * power_int) % BLS_MODULUS - power_int = (power_int * h_int) % BLS_MODULUS - combined_eval_point = BLSFieldElement(combined_eval_int) + combined_eval_point = compute_combined_evaluation(blob.column, h) # 4. Verify the single KZG proof for evaluation at point w^{column_idx} - return kzg.verify_element_proof(combined_eval_point,combined_commitment,blob.combined_column_proof,blob.column_idx,ROOTS_OF_UNITY) + return kzg.verify_element_proof( + combined_eval_point, + combined_commitment, + blob.combined_column_proof, + blob.column_idx, + ROOTS_OF_UNITY + )