mirror of
https://github.com/logos-blockchain/logos-blockchain-specs.git
synced 2026-01-05 06:33:08 +00:00
Update DA encoding/verifier to v1.1
This commit is contained in:
parent
82e5b4da7c
commit
fdce70a9bb
18
da/common.py
18
da/common.py
@ -4,6 +4,7 @@ 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
|
||||
|
||||
|
||||
@ -47,6 +48,23 @@ def build_blob_id(row_commitments: Sequence[Commitment]) -> BlobId:
|
||||
hasher.update(bytes(c))
|
||||
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"
|
||||
|
||||
@ -5,7 +5,7 @@ from hashlib import blake2b
|
||||
|
||||
from eth2spec.eip7594.mainnet import KZGCommitment as Commitment, KZGProof as Proof, BLSFieldElement
|
||||
|
||||
from da.common import ChunksMatrix, Chunk, Row
|
||||
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
|
||||
@ -64,23 +64,16 @@ class DAEncoder:
|
||||
)
|
||||
return ChunksMatrix(__rs_encode_row(row) for row in chunks_matrix)
|
||||
|
||||
def _derive_challenge(self, commitments: Sequence[Commitment]) -> BLSFieldElement:
|
||||
h = blake2b(digest_size=31)
|
||||
h.update(_DST)
|
||||
for com in commitments:
|
||||
h.update(bytes(com))
|
||||
digest31 = h.digest()
|
||||
# pad to 32 bytes
|
||||
return BLSFieldElement.from_bytes(digest31 + b'\x00')
|
||||
@staticmethod
|
||||
def _combined_polynomial(
|
||||
polys: Sequence[Polynomial], h: BLSFieldElement
|
||||
) -> Polynomial:
|
||||
combined = Polynomial([0], BLS_MODULUS)
|
||||
power = BLSFieldElement(1)
|
||||
h_int = int(h) # raw integer challenge
|
||||
int_pow = 1
|
||||
for poly in polys:
|
||||
combined = combined + poly * int(power)
|
||||
power = power * h
|
||||
combined = combined + (poly * int_pow)
|
||||
int_pow = (int_pow * h_int) % BLS_MODULUS
|
||||
return combined
|
||||
|
||||
def _compute_combined_column_proofs(self, combined_poly: Polynomial) -> List[Proof]:
|
||||
@ -94,7 +87,7 @@ class DAEncoder:
|
||||
chunks_matrix = self._chunkify_data(data)
|
||||
row_polynomials, row_commitments = zip(*self._compute_row_kzg_commitments(chunks_matrix))
|
||||
extended_matrix = self._rs_encode_rows(chunks_matrix)
|
||||
h = self._derive_challenge(row_commitments)
|
||||
h = derive_challenge(row_commitments)
|
||||
combined_poly = self._combined_polynomial(row_polynomials, h)
|
||||
combined_column_proofs = self._compute_combined_column_proofs(combined_poly)
|
||||
result = EncodedData(
|
||||
|
||||
@ -49,11 +49,19 @@ class Polynomial[T]:
|
||||
)
|
||||
|
||||
def __mul__(self, other):
|
||||
result = [0] * (len(self.coefficients) + len(other.coefficients) - 1)
|
||||
for i in range(len(self.coefficients)):
|
||||
for j in range(len(other.coefficients)):
|
||||
result[i + j] = (result[i + j] + self.coefficients[i] * other.coefficients[j]) % self.modulus
|
||||
return Polynomial(result, self.modulus)
|
||||
if isinstance(other, int):
|
||||
return Polynomial(
|
||||
[(a * other) % self.modulus for a in self.coefficients],
|
||||
self.modulus
|
||||
)
|
||||
elif isinstance(other, Polynomial):
|
||||
result = [0] * (len(self.coefficients) + len(other.coefficients) - 1)
|
||||
for i in range(len(self.coefficients)):
|
||||
for j in range(len(other.coefficients)):
|
||||
result[i + j] = (result[i + j] + self.coefficients[i] * other.coefficients[j]) % self.modulus
|
||||
return Polynomial(result, self.modulus)
|
||||
else:
|
||||
raise TypeError(f"Unsupported type for multiplication: {type(other)}")
|
||||
|
||||
def divide(self, other):
|
||||
if not isinstance(other, Polynomial):
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
from itertools import chain, batched
|
||||
from random import randrange, 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.encoder import DAEncoderParams, DAEncoder
|
||||
from da.verifier import DAVerifier
|
||||
from eth2spec.eip7594.mainnet import BYTES_PER_FIELD_ELEMENT, BLSFieldElement
|
||||
@ -37,23 +38,23 @@ class TestEncoder(TestCase):
|
||||
self.assertEqual(len(encoded_data.combined_column_proofs), columns_len)
|
||||
|
||||
# verify rows
|
||||
h = DAVerifier._derive_challenge(encoded_data.row_commitments)
|
||||
com_C = encoded_data.row_commitments[0]
|
||||
h = derive_challenge(encoded_data.row_commitments)
|
||||
combined_commitment = encoded_data.row_commitments[0]
|
||||
power = h
|
||||
for com in encoded_data.row_commitments[1:]:
|
||||
com_C = com_C + com * int(power)
|
||||
combined_commitment = bls.add(combined_commitment,bls.multiply(com, power))
|
||||
power = power * h
|
||||
|
||||
for i, (column, proof) in enumerate(zip(encoded_data.extended_matrix.columns, encoded_data.combined_column_proofs)):
|
||||
v = BLSFieldElement(0)
|
||||
combined_eval_point = BLSFieldElement(0)
|
||||
power = BLSFieldElement(1)
|
||||
for chunk in column.chunks:
|
||||
x = BLSFieldElement(int.from_bytes(bytes(chunk), byteorder="big"))
|
||||
v = v + x * power
|
||||
for data in column.chunks:
|
||||
chunk = BLSFieldElement(int.from_bytes(bytes(data), byteorder="big"))
|
||||
combined_eval_point = combined_eval_point + chunk * power
|
||||
power = power * h
|
||||
kzg.verify_element_proof(
|
||||
v,
|
||||
com_C,
|
||||
combined_eval_point,
|
||||
combined_commitment,
|
||||
proof,
|
||||
i,
|
||||
ROOTS_OF_UNITY
|
||||
@ -91,7 +92,7 @@ class TestEncoder(TestCase):
|
||||
def test_generate_combined_column_proofs(self):
|
||||
chunks_matrix = self.encoder._chunkify_data(self.data)
|
||||
row_polynomials, row_commitments = zip(*self.encoder._compute_row_kzg_commitments(chunks_matrix))
|
||||
h = self.encoder._derive_challenge(row_commitments)
|
||||
h = derive_challenge(row_commitments)
|
||||
combined_poly = self.encoder._combined_polynomial(row_polynomials, h)
|
||||
proofs = self.encoder._compute_combined_column_proofs(combined_poly)
|
||||
self.assertEqual(len(proofs), len(row_commitments))
|
||||
|
||||
@ -8,7 +8,7 @@ from eth2spec.eip7594.mainnet import (
|
||||
)
|
||||
|
||||
import da.common
|
||||
from da.common import Column, Chunk, BlobId, build_blob_id
|
||||
from da.common import Column, Chunk, BlobId, build_blob_id, derive_challenge
|
||||
from da.kzg_rs import kzg
|
||||
from da.kzg_rs.common import ROOTS_OF_UNITY, GLOBAL_PARAMETERS, BLS_MODULUS
|
||||
|
||||
@ -26,22 +26,6 @@ class DAShare:
|
||||
return build_blob_id(self.row_commitments)
|
||||
|
||||
class DAVerifier:
|
||||
|
||||
@staticmethod
|
||||
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) || ... )
|
||||
"""
|
||||
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)
|
||||
|
||||
@staticmethod
|
||||
def verify(blob: DAShare) -> bool:
|
||||
"""
|
||||
@ -51,12 +35,12 @@ class DAVerifier:
|
||||
Returns True if verification succeeds, False otherwise.
|
||||
"""
|
||||
# 1. Derive challenge
|
||||
h = DAVerifier._derive_challenge(blob.row_commitments)
|
||||
# 2. Reconstruct combined commitment: com_C = sum_{i=0..l-1} h^i * row_commitments[i]
|
||||
com_C = blob.row_commitments[0]
|
||||
h = derive_challenge(blob.row_commitments)
|
||||
# 2. Reconstruct combined commitment: combined_commitment = sum_{i=0..l-1} h^i * row_commitments[i]
|
||||
combined_commitment = blob.row_commitments[0]
|
||||
power = h
|
||||
for com in blob.row_commitments[1:]:
|
||||
com_C = com_C + com * int(power)
|
||||
combined_commitment = combined_commitment + com * int(power)
|
||||
power = power * h
|
||||
|
||||
# 3. Compute combined evaluation v = sum_{i=0..l-1} (h^i * column_data[i])
|
||||
@ -67,4 +51,4 @@ class DAVerifier:
|
||||
v = v + x * power
|
||||
power = power * h
|
||||
# 4. Verify the single KZG proof for evaluation at point w^{column_idx}
|
||||
return kzg.verify_element_proof(v,com_C,blob.combined_column_proof,blob.column_idx,ROOTS_OF_UNITY)
|
||||
return kzg.verify_element_proof(v,combined_commitment,blob.combined_column_proof,blob.column_idx,ROOTS_OF_UNITY)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user