Added verification to certificate (#82)

* Added verification method to certificate

* Update da/common.py

typo short -> sort

Co-authored-by: gusto <bacv@users.noreply.github.com>

* Fix test imports

* Added verification comment

---------

Co-authored-by: gusto <bacv@users.noreply.github.com>
This commit is contained in:
Daniel Sanchez 2024-03-15 11:34:43 +01:00 committed by GitHub
parent bd964e7b27
commit 0e142c0888
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 32 additions and 24 deletions

View File

@ -1,10 +1,10 @@
from dataclasses import dataclass from dataclasses import dataclass
from itertools import chain, zip_longest from hashlib import sha3_256
from typing import List, Generator, Self from itertools import chain, zip_longest, compress
from typing import List, Generator, Self, Sequence
from eth2spec.eip7594.mainnet import Bytes32, KZGCommitment as Commitment from eth2spec.eip7594.mainnet import Bytes32, KZGCommitment as Commitment
from py_ecc.bls import G2ProofOfPossession as bls_pop
class NodeId(Bytes32): class NodeId(Bytes32):
@ -55,3 +55,21 @@ class Certificate:
aggregated_column_commitment: Commitment aggregated_column_commitment: Commitment
row_commitments: List[Commitment] row_commitments: List[Commitment]
def verify(self, nodes_public_keys: List[BLSPublickey]) -> bool:
"""
List of nodes public keys should be a trusted list of verified proof of possession keys.
Otherwise, we could fall under the Rogue Key Attack
`assert all(bls_pop.PopVerify(pk, proof) for pk, proof in zip(node_public_keys, pops))`
"""
# we sort them as the signers bitfield is sorted by the public keys as well
signers_keys = list(compress(sorted(nodes_public_keys), self.signers))
message = build_attestation_message(self.aggregated_column_commitment, self.row_commitments)
return bls_pop.AggregateVerify(signers_keys, [message]*len(signers_keys), self.aggregated_signatures)
def build_attestation_message(aggregated_column_commitment: Commitment, row_commitments: Sequence[Commitment]) -> bytes:
hasher = sha3_256()
hasher.update(bytes(aggregated_column_commitment))
for c in row_commitments:
hasher.update(bytes(c))
return hasher.digest()

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, Bitfield from da.common import Certificate, NodeId, BLSPublickey, Bitfield, build_attestation_message
from da.encoder import EncodedData from da.encoder import EncodedData
from da.verifier import DABlob, Attestation from da.verifier import DABlob, Attestation
@ -19,9 +19,9 @@ 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 # sort over public keys
self.settings.nodes_ids, self.settings.nodes_pubkey = zip( self.settings.nodes_ids, self.settings.nodes_pubkey = zip(
*sorted(zip(self.settings.nodes_ids, self.settings.nodes_pubkey), key=lambda x: x[0]) *sorted(zip(self.settings.nodes_ids, self.settings.nodes_pubkey), key=lambda x: x[1])
) )
def _prepare_data(self, encoded_data: EncodedData) -> Generator[DABlob, None, None]: def _prepare_data(self, encoded_data: EncodedData) -> Generator[DABlob, None, None]:
@ -71,11 +71,7 @@ class Dispersal:
@staticmethod @staticmethod
def _build_attestation_message(encoded_data: EncodedData) -> bytes: def _build_attestation_message(encoded_data: EncodedData) -> bytes:
hasher = sha3_256() return build_attestation_message(encoded_data.aggregated_column_commitment, encoded_data.row_commitments)
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]: def disperse(self, encoded_data: EncodedData) -> Optional[Certificate]:
attestations = [] attestations = []

View File

@ -1,14 +1,13 @@
from hashlib import sha3_256 from hashlib import sha3_256
from unittest import TestCase from unittest import TestCase
from .encoder import DAEncoderParams, DAEncoder from da.encoder import DAEncoderParams, DAEncoder
from .test_encoder import TestEncoder from da.test_encoder import TestEncoder
from da.verifier import DAVerifier, DABlob
from da.common import NodeId, Attestation, Bitfield 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 .verifier import DAVerifier, DABlob from py_ecc.bls import G2ProofOfPossession as bls_pop
class TestDispersal(TestCase): class TestDispersal(TestCase):
@ -46,7 +45,7 @@ class TestDispersal(TestCase):
self.assertEqual(certificate.row_commitments, []) self.assertEqual(certificate.row_commitments, [])
self.assertIsNotNone(certificate.aggregated_signatures) self.assertIsNotNone(certificate.aggregated_signatures)
self.assertTrue( self.assertTrue(
bls_pop.AggregateVerify(self.public_keys, [mock_message]*len(self.public_keys), certificate.aggregated_signatures) certificate.verify(self.public_keys)
) )
def test_disperse(self): def test_disperse(self):
@ -64,12 +63,7 @@ class TestDispersal(TestCase):
certificate = self.dispersal.disperse(encoded_data) certificate = self.dispersal.disperse(encoded_data)
self.assertIsNotNone(certificate) self.assertIsNotNone(certificate)
self.assertTrue( self.assertTrue(certificate.verify(self.public_keys)
bls_pop.AggregateVerify(
self.public_keys[:self.dispersal.settings.threshold],
[self.dispersal._build_attestation_message(encoded_data)]*self.dispersal.settings.threshold,
certificate.aggregated_signatures
)
) )
self.assertEqual( self.assertEqual(
certificate.signers, certificate.signers,