mirror of
https://github.com/logos-blockchain/logos-blockchain-specs.git
synced 2026-01-06 23:23:09 +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 typing import List, Generator, Self, Sequence
|
||||||
|
|
||||||
from eth2spec.eip7594.mainnet import Bytes32, KZGCommitment as Commitment
|
from eth2spec.eip7594.mainnet import Bytes32, KZGCommitment as Commitment
|
||||||
|
from eth2spec.eip7594.mainnet import BLSFieldElement
|
||||||
from py_ecc.bls import G2ProofOfPossession
|
from py_ecc.bls import G2ProofOfPossession
|
||||||
|
|
||||||
|
|
||||||
@ -47,6 +48,23 @@ def build_blob_id(row_commitments: Sequence[Commitment]) -> BlobId:
|
|||||||
hasher.update(bytes(c))
|
hasher.update(bytes(c))
|
||||||
return hasher.digest()
|
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):
|
class NomosDaG2ProofOfPossession(G2ProofOfPossession):
|
||||||
# Domain specific tag for Nomos DA protocol
|
# Domain specific tag for Nomos DA protocol
|
||||||
DST = b"NOMOS_DA_AVAIL"
|
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 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 import kzg, rs
|
||||||
from da.kzg_rs.common import GLOBAL_PARAMETERS, ROOTS_OF_UNITY, BYTES_PER_FIELD_ELEMENT, BLS_MODULUS
|
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.poly import Polynomial
|
||||||
@ -64,23 +64,16 @@ class DAEncoder:
|
|||||||
)
|
)
|
||||||
return ChunksMatrix(__rs_encode_row(row) for row in chunks_matrix)
|
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
|
@staticmethod
|
||||||
def _combined_polynomial(
|
def _combined_polynomial(
|
||||||
polys: Sequence[Polynomial], h: BLSFieldElement
|
polys: Sequence[Polynomial], h: BLSFieldElement
|
||||||
) -> Polynomial:
|
) -> Polynomial:
|
||||||
combined = Polynomial([0], BLS_MODULUS)
|
combined = Polynomial([0], BLS_MODULUS)
|
||||||
power = BLSFieldElement(1)
|
h_int = int(h) # raw integer challenge
|
||||||
|
int_pow = 1
|
||||||
for poly in polys:
|
for poly in polys:
|
||||||
combined = combined + poly * int(power)
|
combined = combined + (poly * int_pow)
|
||||||
power = power * h
|
int_pow = (int_pow * h_int) % BLS_MODULUS
|
||||||
return combined
|
return combined
|
||||||
|
|
||||||
def _compute_combined_column_proofs(self, combined_poly: Polynomial) -> List[Proof]:
|
def _compute_combined_column_proofs(self, combined_poly: Polynomial) -> List[Proof]:
|
||||||
@ -94,7 +87,7 @@ class DAEncoder:
|
|||||||
chunks_matrix = self._chunkify_data(data)
|
chunks_matrix = self._chunkify_data(data)
|
||||||
row_polynomials, row_commitments = zip(*self._compute_row_kzg_commitments(chunks_matrix))
|
row_polynomials, row_commitments = zip(*self._compute_row_kzg_commitments(chunks_matrix))
|
||||||
extended_matrix = self._rs_encode_rows(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_poly = self._combined_polynomial(row_polynomials, h)
|
||||||
combined_column_proofs = self._compute_combined_column_proofs(combined_poly)
|
combined_column_proofs = self._compute_combined_column_proofs(combined_poly)
|
||||||
result = EncodedData(
|
result = EncodedData(
|
||||||
|
|||||||
@ -49,11 +49,19 @@ class Polynomial[T]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
result = [0] * (len(self.coefficients) + len(other.coefficients) - 1)
|
if isinstance(other, int):
|
||||||
for i in range(len(self.coefficients)):
|
return Polynomial(
|
||||||
for j in range(len(other.coefficients)):
|
[(a * other) % self.modulus for a in self.coefficients],
|
||||||
result[i + j] = (result[i + j] + self.coefficients[i] * other.coefficients[j]) % self.modulus
|
self.modulus
|
||||||
return Polynomial(result, 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):
|
def divide(self, other):
|
||||||
if not isinstance(other, Polynomial):
|
if not isinstance(other, Polynomial):
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
from itertools import chain, batched
|
from itertools import chain, batched
|
||||||
from random import randrange, randbytes
|
from random import randrange, randbytes
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
from eth2spec.utils import bls
|
||||||
from eth2spec.deneb.mainnet import bytes_to_bls_field
|
from eth2spec.deneb.mainnet import bytes_to_bls_field
|
||||||
|
|
||||||
from da import encoder
|
from da import encoder
|
||||||
|
from da.common import derive_challenge
|
||||||
from da.encoder import DAEncoderParams, DAEncoder
|
from da.encoder import DAEncoderParams, DAEncoder
|
||||||
from da.verifier import DAVerifier
|
from da.verifier import DAVerifier
|
||||||
from eth2spec.eip7594.mainnet import BYTES_PER_FIELD_ELEMENT, BLSFieldElement
|
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)
|
self.assertEqual(len(encoded_data.combined_column_proofs), columns_len)
|
||||||
|
|
||||||
# verify rows
|
# verify rows
|
||||||
h = DAVerifier._derive_challenge(encoded_data.row_commitments)
|
h = derive_challenge(encoded_data.row_commitments)
|
||||||
com_C = encoded_data.row_commitments[0]
|
combined_commitment = encoded_data.row_commitments[0]
|
||||||
power = h
|
power = h
|
||||||
for com in encoded_data.row_commitments[1:]:
|
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
|
power = power * h
|
||||||
|
|
||||||
for i, (column, proof) in enumerate(zip(encoded_data.extended_matrix.columns, encoded_data.combined_column_proofs)):
|
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)
|
power = BLSFieldElement(1)
|
||||||
for chunk in column.chunks:
|
for data in column.chunks:
|
||||||
x = BLSFieldElement(int.from_bytes(bytes(chunk), byteorder="big"))
|
chunk = BLSFieldElement(int.from_bytes(bytes(data), byteorder="big"))
|
||||||
v = v + x * power
|
combined_eval_point = combined_eval_point + chunk * power
|
||||||
power = power * h
|
power = power * h
|
||||||
kzg.verify_element_proof(
|
kzg.verify_element_proof(
|
||||||
v,
|
combined_eval_point,
|
||||||
com_C,
|
combined_commitment,
|
||||||
proof,
|
proof,
|
||||||
i,
|
i,
|
||||||
ROOTS_OF_UNITY
|
ROOTS_OF_UNITY
|
||||||
@ -91,7 +92,7 @@ class TestEncoder(TestCase):
|
|||||||
def test_generate_combined_column_proofs(self):
|
def test_generate_combined_column_proofs(self):
|
||||||
chunks_matrix = self.encoder._chunkify_data(self.data)
|
chunks_matrix = self.encoder._chunkify_data(self.data)
|
||||||
row_polynomials, row_commitments = zip(*self.encoder._compute_row_kzg_commitments(chunks_matrix))
|
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)
|
combined_poly = self.encoder._combined_polynomial(row_polynomials, h)
|
||||||
proofs = self.encoder._compute_combined_column_proofs(combined_poly)
|
proofs = self.encoder._compute_combined_column_proofs(combined_poly)
|
||||||
self.assertEqual(len(proofs), len(row_commitments))
|
self.assertEqual(len(proofs), len(row_commitments))
|
||||||
|
|||||||
@ -8,7 +8,7 @@ from eth2spec.eip7594.mainnet import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
import da.common
|
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 import kzg
|
||||||
from da.kzg_rs.common import ROOTS_OF_UNITY, GLOBAL_PARAMETERS, BLS_MODULUS
|
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)
|
return build_blob_id(self.row_commitments)
|
||||||
|
|
||||||
class DAVerifier:
|
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
|
@staticmethod
|
||||||
def verify(blob: DAShare) -> bool:
|
def verify(blob: DAShare) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -51,12 +35,12 @@ class DAVerifier:
|
|||||||
Returns True if verification succeeds, False otherwise.
|
Returns True if verification succeeds, False otherwise.
|
||||||
"""
|
"""
|
||||||
# 1. Derive challenge
|
# 1. Derive challenge
|
||||||
h = DAVerifier._derive_challenge(blob.row_commitments)
|
h = derive_challenge(blob.row_commitments)
|
||||||
# 2. Reconstruct combined commitment: com_C = sum_{i=0..l-1} h^i * row_commitments[i]
|
# 2. Reconstruct combined commitment: combined_commitment = sum_{i=0..l-1} h^i * row_commitments[i]
|
||||||
com_C = blob.row_commitments[0]
|
combined_commitment = blob.row_commitments[0]
|
||||||
power = h
|
power = h
|
||||||
for com in blob.row_commitments[1:]:
|
for com in blob.row_commitments[1:]:
|
||||||
com_C = com_C + com * int(power)
|
combined_commitment = combined_commitment + com * int(power)
|
||||||
power = power * h
|
power = power * h
|
||||||
|
|
||||||
# 3. Compute combined evaluation v = sum_{i=0..l-1} (h^i * column_data[i])
|
# 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
|
v = v + x * power
|
||||||
power = power * h
|
power = power * h
|
||||||
# 4. Verify the single KZG proof for evaluation at point w^{column_idx}
|
# 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