diff --git a/da/dispersal.py b/da/dispersal.py index eb721ff..9bab1e6 100644 --- a/da/dispersal.py +++ b/da/dispersal.py @@ -10,7 +10,6 @@ from da.verifier import DABlob, Attestation @dataclass class DispersalSettings: nodes_ids: List[NodeId] - nodes_pubkey: List[BLSPublicKey] threshold: int @@ -43,48 +42,16 @@ class Dispersal: ) yield blob - def _send_and_await_response(self, node: NodeId, blob: DABlob) -> Optional[Attestation]: + def _send_and_await_response(self, node: NodeId, blob: DABlob) -> bool: pass - def _build_certificate( - self, - encoded_data: EncodedData, - attestations: Sequence[Attestation], - signers: Bitfield - ) -> Certificate: - assert len(attestations) >= self.settings.threshold - assert len(attestations) == signers.count(True) - aggregated = bls_pop.Aggregate([attestation.signature for attestation in attestations]) - return Certificate( - aggregated_signatures=aggregated, - signers=signers, - aggregated_column_commitment=encoded_data.aggregated_column_commitment, - row_commitments=encoded_data.row_commitments - ) - - @staticmethod - def _verify_attestation(public_key: BLSPublicKey, attested_message: bytes, attestation: Attestation) -> bool: - return bls_pop.Verify(public_key, attested_message, attestation.signature) - - @staticmethod - def _build_attestation_message(encoded_data: EncodedData) -> bytes: - return build_blob_id(encoded_data.aggregated_column_commitment, encoded_data.row_commitments) - - def disperse(self, encoded_data: EncodedData) -> Optional[Certificate]: + def disperse(self, encoded_data: EncodedData): attestations = [] - attested_message = self._build_attestation_message(encoded_data) - signed = Bitfield(False for _ in range(len(self.settings.nodes_ids))) blob_data = zip( range(len(self.settings.nodes_ids)), self.settings.nodes_ids, - self.settings.nodes_pubkey, self._prepare_data(encoded_data) ) - for i, node, pk, blob in blob_data: - if attestation := self._send_and_await_response(node, blob): - if self._verify_attestation(pk, attested_message, attestation): - # mark as received - signed[i] = True - attestations.append(attestation) - if len(attestations) >= self.settings.threshold: - return self._build_certificate(encoded_data, attestations, signed) + for i, node, blob in blob_data: + self._send_and_await_response(node, blob) + diff --git a/da/test_dispersal.py b/da/test_dispersal.py index a4d59b6..e8d1ec6 100644 --- a/da/test_dispersal.py +++ b/da/test_dispersal.py @@ -4,71 +4,33 @@ from unittest import TestCase from da.encoder import DAEncoderParams, DAEncoder from da.test_encoder import TestEncoder from da.verifier import DAVerifier, DABlob -from da.common import NodeId, Attestation, Bitfield, NomosDaG2ProofOfPossession as bls_pop -from da.dispersal import Dispersal, EncodedData, DispersalSettings +from da.common import NodeId, NomosDaG2ProofOfPossession as bls_pop +from da.dispersal import Dispersal, DispersalSettings class TestDispersal(TestCase): def setUp(self): self.n_nodes = 16 self.nodes_ids = [NodeId(x.to_bytes(length=32, byteorder='big')) for x in range(self.n_nodes)] - self.secret_keys = list(range(1, self.n_nodes+1)) - self.public_keys = [bls_pop.SkToPk(sk) for sk in self.secret_keys] - # sort by pk as we do in dispersal - self.secret_keys, self.public_keys = zip( - *sorted(zip(self.secret_keys, self.public_keys), key=lambda x: x[1]) - ) dispersal_settings = DispersalSettings( self.nodes_ids, - self.public_keys, self.n_nodes // 2 + 1 ) self.dispersal = Dispersal(dispersal_settings) self.encoder_test = TestEncoder() self.encoder_test.setUp() - def test_build_certificate_insufficient_attestations(self): - with self.assertRaises(AssertionError): - self.dispersal._build_certificate(None, [], []) - - def test_build_certificate_enough_attestations(self): - mock_encoded_data = EncodedData( - None, None, None, [], [], [], bytes(b"f"*48), [] - ) - mock_message = sha3_256(mock_encoded_data.aggregated_column_commitment).digest() - mock_attestations = [Attestation(bls_pop.Sign(sk, mock_message)) for sk in self.secret_keys] - certificate = self.dispersal._build_certificate( - mock_encoded_data, - mock_attestations, - Bitfield([True for _ in range(len(self.secret_keys))]) - ) - self.assertIsNotNone(certificate) - self.assertEqual(certificate.aggregated_column_commitment, mock_encoded_data.aggregated_column_commitment) - self.assertEqual(certificate.row_commitments, []) - self.assertIsNotNone(certificate.aggregated_signatures) - self.assertTrue( - certificate.verify(self.public_keys) - ) - def test_disperse(self): data = self.encoder_test.data encoding_params = DAEncoderParams(column_count=self.n_nodes // 2, bytes_per_chunk=31) encoded_data = DAEncoder(encoding_params).encode(data) # mock send and await method with local verifiers - def __send_and_await_response(node: NodeId, blob: DABlob): - sk = self.secret_keys[int.from_bytes(node)] - verifier = DAVerifier(sk, self.public_keys) + def __send_and_await_response(blob: DABlob): + verifier = DAVerifier() return verifier.verify(blob) # inject mock send and await method self.dispersal._send_and_await_response = __send_and_await_response - certificate = self.dispersal.disperse(encoded_data) - self.assertIsNotNone(certificate) - self.assertTrue(certificate.verify(self.public_keys) - ) - self.assertEqual( - certificate.signers, - [True if i < self.dispersal.settings.threshold else False for i in range(self.n_nodes)] - ) + self.assertTrue(self.dispersal.disperse(encoded_data)) diff --git a/da/verifier.py b/da/verifier.py index 1ba5350..fd6da89 100644 --- a/da/verifier.py +++ b/da/verifier.py @@ -18,6 +18,7 @@ from da.kzg_rs.common import ROOTS_OF_UNITY, GLOBAL_PARAMETERS, BLS_MODULUS @dataclass class DABlob: column: Column + column_idx: int column_commitment: Commitment aggregated_column_commitment: Commitment aggregated_column_proof: Proof @@ -32,17 +33,16 @@ class DABlob: class DAVerifier: - def __init__(self, nodes_pks: List[BLSPublicKey]): + def __init__(self): self.attested_blobs: Set[BlobId] = set() - self.index = nodes_pks.index(bls_pop.SkToPk(self.sk)) @staticmethod def _verify_column( column: Column, + column_idx: int, column_commitment: Commitment, aggregated_column_commitment: Commitment, aggregated_column_proof: Proof, - index: int ) -> bool: # 1. compute commitment for column _, computed_column_commitment = kzg.bytes_to_commitment(column.as_bytes(), GLOBAL_PARAMETERS) @@ -54,7 +54,7 @@ class DAVerifier: # 4. Check proof with commitment and proof over the aggregated column commitment chunk = BLSFieldElement.from_bytes(column_hash) return kzg.verify_element_proof( - chunk, aggregated_column_commitment, aggregated_column_proof, index, ROOTS_OF_UNITY + chunk, aggregated_column_commitment, aggregated_column_proof, column_idx, ROOTS_OF_UNITY ) @staticmethod @@ -78,25 +78,21 @@ class DAVerifier: def verify(self, blob: DABlob) -> bool: blob_id = blob.blob_id() - if previous_attestation := self.attested_blobs.get(blob_id): - column_id, attestation = previous_attestation - # we already attested, is cached so we return it - if column_id == blob.column_id(): - return attestation - # we already attested and they are asking us to attest the same data different column + if blob_id in self.attested_blobs: + # we already attested and they are asking us to attest again # skip return False is_column_verified = DAVerifier._verify_column( blob.column, + blob.column_idx, blob.column_commitment, blob.aggregated_column_commitment, blob.aggregated_column_proof, - self.index ) if not is_column_verified: return False are_chunks_verified = DAVerifier._verify_chunks( - blob.column, blob.rows_commitments, blob.rows_proofs, self.index + blob.column, blob.rows_commitments, blob.rows_proofs, blob.column_idx ) if not are_chunks_verified: return False