Threshold bitfield on certificate (#81)

* Added threshold bitfield to certificate

* Short nodes_ids and public_keys
This commit is contained in:
Daniel Sanchez 2024-03-14 12:20:46 +01:00 committed by GitHub
parent 8c34f8a39e
commit bd964e7b27
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 40 additions and 7 deletions

View File

@ -39,6 +39,10 @@ BLSPrivateKey = int
BLSSignature = bytes BLSSignature = bytes
class Bitfield(List[bool]):
pass
@dataclass @dataclass
class Attestation: class Attestation:
signature: BLSSignature signature: BLSSignature
@ -47,6 +51,7 @@ class Attestation:
@dataclass @dataclass
class Certificate: class Certificate:
aggregated_signatures: BLSSignature aggregated_signatures: BLSSignature
signers: Bitfield
aggregated_column_commitment: Commitment aggregated_column_commitment: Commitment
row_commitments: List[Commitment] row_commitments: List[Commitment]

View File

@ -4,7 +4,7 @@ from typing import List, Optional, Generator, Sequence
from py_ecc.bls import G2ProofOfPossession as bls_pop from py_ecc.bls import G2ProofOfPossession as bls_pop
from da.common import Certificate, NodeId, BLSPublickey from da.common import Certificate, NodeId, BLSPublickey, Bitfield
from da.encoder import EncodedData from da.encoder import EncodedData
from da.verifier import DABlob, Attestation from da.verifier import DABlob, Attestation
@ -19,6 +19,10 @@ class DispersalSettings:
class Dispersal: class Dispersal:
def __init__(self, settings: DispersalSettings): def __init__(self, settings: DispersalSettings):
self.settings = settings self.settings = settings
# sort nodes_ids and related public keys
self.settings.nodes_ids, self.settings.nodes_pubkey = zip(
*sorted(zip(self.settings.nodes_ids, self.settings.nodes_pubkey), key=lambda x: x[0])
)
def _prepare_data(self, encoded_data: EncodedData) -> Generator[DABlob, None, None]: def _prepare_data(self, encoded_data: EncodedData) -> Generator[DABlob, None, None]:
assert len(encoded_data.column_commitments) == len(self.settings.nodes_ids) assert len(encoded_data.column_commitments) == len(self.settings.nodes_ids)
@ -45,11 +49,18 @@ class Dispersal:
def _send_and_await_response(self, node: NodeId, blob: DABlob) -> Optional[Attestation]: def _send_and_await_response(self, node: NodeId, blob: DABlob) -> Optional[Attestation]:
pass pass
def _build_certificate(self, encoded_data: EncodedData, attestations: Sequence[Attestation]) -> Certificate: def _build_certificate(
self,
encoded_data: EncodedData,
attestations: Sequence[Attestation],
signers: Bitfield
) -> Certificate:
assert len(attestations) >= self.settings.threshold assert len(attestations) >= self.settings.threshold
assert len(attestations) == signers.count(True)
aggregated = bls_pop.Aggregate([attestation.signature for attestation in attestations]) aggregated = bls_pop.Aggregate([attestation.signature for attestation in attestations])
return Certificate( return Certificate(
aggregated_signatures=aggregated, aggregated_signatures=aggregated,
signers=signers,
aggregated_column_commitment=encoded_data.aggregated_column_commitment, aggregated_column_commitment=encoded_data.aggregated_column_commitment,
row_commitments=encoded_data.row_commitments row_commitments=encoded_data.row_commitments
) )
@ -69,9 +80,18 @@ class Dispersal:
def disperse(self, encoded_data: EncodedData) -> Optional[Certificate]: def disperse(self, encoded_data: EncodedData) -> Optional[Certificate]:
attestations = [] attestations = []
attested_message = self._build_attestation_message(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)): 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 attestation := self._send_and_await_response(node, blob):
if self._verify_attestation(pk, attested_message, attestation): if self._verify_attestation(pk, attested_message, attestation):
# mark as received
signed[i] = True
attestations.append(attestation) attestations.append(attestation)
if len(attestations) >= self.settings.threshold: if len(attestations) >= self.settings.threshold:
return self._build_certificate(encoded_data, attestations) return self._build_certificate(encoded_data, attestations, signed)

View File

@ -4,7 +4,7 @@ from unittest import TestCase
from .encoder import DAEncoderParams, DAEncoder from .encoder import DAEncoderParams, DAEncoder
from .test_encoder import TestEncoder from .test_encoder import TestEncoder
from da.common import NodeId, Attestation from da.common import NodeId, Attestation, Bitfield
from da.dispersal import Dispersal, EncodedData, DispersalSettings from da.dispersal import Dispersal, EncodedData, DispersalSettings
from py_ecc.bls import G2ProofOfPossession as bls_pop from py_ecc.bls import G2ProofOfPossession as bls_pop
@ -28,7 +28,7 @@ class TestDispersal(TestCase):
def test_build_certificate_insufficient_attestations(self): def test_build_certificate_insufficient_attestations(self):
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
self.dispersal._build_certificate(None, []) self.dispersal._build_certificate(None, [], [])
def test_build_certificate_enough_attestations(self): def test_build_certificate_enough_attestations(self):
mock_encoded_data = EncodedData( mock_encoded_data = EncodedData(
@ -36,7 +36,11 @@ class TestDispersal(TestCase):
) )
mock_message = sha3_256(mock_encoded_data.aggregated_column_commitment).digest() 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] 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) certificate = self.dispersal._build_certificate(
mock_encoded_data,
mock_attestations,
Bitfield([True for _ in range(len(self.secret_keys))])
)
self.assertIsNotNone(certificate) self.assertIsNotNone(certificate)
self.assertEqual(certificate.aggregated_column_commitment, mock_encoded_data.aggregated_column_commitment) self.assertEqual(certificate.aggregated_column_commitment, mock_encoded_data.aggregated_column_commitment)
self.assertEqual(certificate.row_commitments, []) self.assertEqual(certificate.row_commitments, [])
@ -67,4 +71,8 @@ class TestDispersal(TestCase):
certificate.aggregated_signatures certificate.aggregated_signatures
) )
) )
self.assertEqual(
certificate.signers,
[True if i < self.dispersal.settings.threshold else False for i in range(self.n_nodes)]
)