diff --git a/da/test_verifier.py b/da/test_verifier.py index 747b6ae..fd29aa2 100644 --- a/da/test_verifier.py +++ b/da/test_verifier.py @@ -26,14 +26,12 @@ class TestVerifier(TestCase): ) ) - def test_build_attestation(self): - pass - def test_verify(self): _ = TestEncoder() _.setUp() encoded_data = _.encoder.encode(_.data) for i, column in enumerate(encoded_data.chunked_data.columns): + verifier = DAVerifier(1987) da_blob = DABlob( i, Column(column), @@ -43,4 +41,32 @@ class TestVerifier(TestCase): encoded_data.row_commitments, [row[i] for row in encoded_data.row_proofs], ) - self.assertIsNotNone(self.verifier.verify(da_blob)) + self.assertIsNotNone(verifier.verify(da_blob)) + + def test_verify_duplicated_blob(self): + _ = TestEncoder() + _.setUp() + encoded_data = _.encoder.encode(_.data) + columns = enumerate(encoded_data.chunked_data.columns) + i, column = next(columns) + da_blob = DABlob( + i, + Column(column), + encoded_data.column_commitments[i], + encoded_data.aggregated_column_commitment, + encoded_data.aggregated_column_proofs[i], + encoded_data.row_commitments, + [row[i] for row in encoded_data.row_proofs], + ) + self.assertIsNotNone(self.verifier.verify(da_blob)) + for i, column in columns: + da_blob = DABlob( + i, + Column(column), + encoded_data.column_commitments[i], + encoded_data.aggregated_column_commitment, + encoded_data.aggregated_column_proofs[i], + encoded_data.row_commitments, + [row[i] for row in encoded_data.row_proofs], + ) + self.assertIsNone(self.verifier.verify(da_blob)) diff --git a/da/verifier.py b/da/verifier.py index e9d483b..46b4dbe 100644 --- a/da/verifier.py +++ b/da/verifier.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from hashlib import sha3_256 -from typing import List, Optional, Sequence +from typing import List, Optional, Sequence, Set from eth2spec.deneb.mainnet import BLSFieldElement from eth2spec.eip7594.mainnet import ( @@ -9,6 +9,7 @@ from eth2spec.eip7594.mainnet import ( ) from py_ecc.bls import G2ProofOfPossession as bls_pop +import da.common from da.common import Column, Chunk, Attestation, BLSPrivateKey from da.encoder import DAEncoder from da.kzg_rs import kzg @@ -25,9 +26,13 @@ class DABlob: rows_commitments: List[Commitment] rows_proofs: List[Proof] + def id(self): + return da.common.build_attestation_message(self.aggregated_column_commitment, self.rows_commitments) + class DAVerifier: def __init__(self, sk: BLSPrivateKey): + self.attested_blobs: Set[bytes] = set() self.sk = sk @staticmethod @@ -79,6 +84,9 @@ class DAVerifier: return Attestation(signature=bls_pop.Sign(self.sk, message)) def verify(self, blob: DABlob) -> Optional[Attestation]: + if (blob_id := blob.id()) in self.attested_blobs: + # We already attested for such blob, skip + return None is_column_verified = DAVerifier._verify_column( blob.column, blob.column_commitment, @@ -93,4 +101,5 @@ class DAVerifier: ) if not are_chunks_verified: return + self.attested_blobs.add(blob_id) return self._build_attestation(blob)