-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added common bls types * Added verifier attestation build * Implement verification and aggregation in dispersal * Added certificate building tests * Added dispersal test
- Loading branch information
1 parent
7ffb7cc
commit 8c34f8a
Showing
6 changed files
with
127 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,77 @@ | ||
from typing import List, Optional, Generator | ||
from dataclasses import dataclass | ||
from hashlib import sha3_256 | ||
from typing import List, Optional, Generator, Sequence | ||
|
||
from da.common import Certificate, NodeId | ||
from py_ecc.bls import G2ProofOfPossession as bls_pop | ||
|
||
from da.common import Certificate, NodeId, BLSPublickey | ||
from da.encoder import EncodedData | ||
from da.verifier import DABlob, Attestation | ||
|
||
|
||
@dataclass | ||
class DispersalSettings: | ||
nodes_ids: List[NodeId] | ||
nodes_pubkey: List[BLSPublickey] | ||
threshold: int | ||
|
||
|
||
class Dispersal: | ||
def __init__(self, nodes: List[NodeId], threshold: int): | ||
self.nodes = nodes | ||
self.threshold = threshold | ||
def __init__(self, settings: DispersalSettings): | ||
self.settings = settings | ||
|
||
def _prepare_data(self, encoded_data: EncodedData) -> Generator[DABlob, None, None]: | ||
assert len(encoded_data.row_commitments) == len(self.nodes) | ||
assert len(encoded_data.row_proofs) == len(self.nodes) | ||
assert len(encoded_data.column_commitments) == len(self.settings.nodes_ids) | ||
assert len(encoded_data.aggregated_column_proofs) == len(self.settings.nodes_ids) | ||
columns = encoded_data.extended_matrix.columns | ||
column_commitments = encoded_data.column_commitments | ||
row_commitments = encoded_data.row_commitments | ||
rows_proofs = encoded_data.row_proofs | ||
aggregated_column_commitment = encoded_data.aggregated_column_commitment | ||
aggregated_column_proof = encoded_data.aggregated_column_proof | ||
for index, (column, column_commitment, row_proofs) in enumerate(zip(columns, column_commitments, rows_proofs)): | ||
aggregated_column_proofs = encoded_data.aggregated_column_proofs | ||
blobs_data = enumerate(zip(columns, column_commitments, zip(*rows_proofs), aggregated_column_proofs)) | ||
for index, (column, column_commitment, row_proofs, column_proof) in blobs_data: | ||
blob = DABlob( | ||
index, | ||
column, | ||
column_commitment, | ||
aggregated_column_commitment, | ||
aggregated_column_proof, | ||
column_proof, | ||
row_commitments, | ||
row_proofs | ||
) | ||
yield blob | ||
|
||
def _send_and_await_response(self, node, encoded_data: EncodedData) -> Optional[Attestation]: | ||
def _send_and_await_response(self, node: NodeId, blob: DABlob) -> Optional[Attestation]: | ||
pass | ||
|
||
def _build_certificate(self, attestations: List[Attestation]): | ||
pass | ||
def _build_certificate(self, encoded_data: EncodedData, attestations: Sequence[Attestation]) -> Certificate: | ||
assert len(attestations) >= self.settings.threshold | ||
aggregated = bls_pop.Aggregate([attestation.signature for attestation in attestations]) | ||
return Certificate( | ||
aggregated_signatures=aggregated, | ||
aggregated_column_commitment=encoded_data.aggregated_column_commitment, | ||
row_commitments=encoded_data.row_commitments | ||
) | ||
|
||
def _verify_attestation(self, attestation: Attestation) -> bool: | ||
pass | ||
@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: | ||
hasher = sha3_256() | ||
hasher.update(bytes(encoded_data.aggregated_column_commitment)) | ||
for c in encoded_data.row_commitments: | ||
hasher.update(bytes(c)) | ||
return hasher.digest() | ||
|
||
def disperse(self, encoded_data: EncodedData) -> Optional[Certificate]: | ||
attestations = [] | ||
for node, blob in zip(self.nodes, self._prepare_data(encoded_data)): | ||
attested_message = self._build_attestation_message(encoded_data) | ||
for node, pk, blob in zip(self.settings.nodes_ids, self.settings.nodes_pubkey, self._prepare_data(encoded_data)): | ||
if attestation := self._send_and_await_response(node, blob): | ||
if self._verify_attestation(attestation): | ||
if self._verify_attestation(pk, attested_message, attestation): | ||
attestations.append(attestation) | ||
if len(attestations) >= self.threshold: | ||
return self._build_certificate(attestations) | ||
if len(attestations) >= self.settings.threshold: | ||
return self._build_certificate(encoded_data, attestations) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,70 @@ | ||
from hashlib import sha3_256 | ||
from unittest import TestCase | ||
from da.dispersal import Dispersal, DABlob, EncodedData | ||
|
||
from .encoder import DAEncoderParams, DAEncoder | ||
from .test_encoder import TestEncoder | ||
|
||
from da.common import NodeId, Attestation | ||
from da.dispersal import Dispersal, EncodedData, DispersalSettings | ||
from py_ecc.bls import G2ProofOfPossession as bls_pop | ||
|
||
from .verifier import DAVerifier, DABlob | ||
|
||
|
||
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] | ||
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): | ||
pass | ||
with self.assertRaises(AssertionError): | ||
self.dispersal._build_certificate(None, []) | ||
|
||
def test_build_certificate_enough_attestations(self): | ||
pass | ||
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) | ||
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( | ||
bls_pop.AggregateVerify(self.public_keys, [mock_message]*len(self.public_keys), certificate.aggregated_signatures) | ||
) | ||
|
||
def test_prepare_data(self): | ||
pass | ||
def test_disperse(self): | ||
data = self.encoder_test.data | ||
encoding_params = DAEncoderParams(column_count=self.n_nodes // 2, bytes_per_field_element=32) | ||
encoded_data = DAEncoder(encoding_params).encode(data) | ||
|
||
def test_verify_attestation(self): | ||
pass | ||
# 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) | ||
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( | ||
bls_pop.AggregateVerify( | ||
self.public_keys[:self.dispersal.settings.threshold], | ||
[self.dispersal._build_attestation_message(encoded_data)]*self.dispersal.settings.threshold, | ||
certificate.aggregated_signatures | ||
) | ||
) | ||
|
||
def test_disperse(self): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters