Threshold bitfield on certificate (#81)
* Added threshold bitfield to certificate * Short nodes_ids and public_keys
This commit is contained in:
parent
8c34f8a39e
commit
bd964e7b27
|
@ -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]
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue