From 026b2f52e631664a298fe524c2dbfad83d746020 Mon Sep 17 00:00:00 2001 From: Chih Cheng Liang Date: Mon, 6 May 2019 16:17:30 +0800 Subject: [PATCH] fast-verification-of-multiple-bls-signatures --- py_ecc/bls/__init__.py | 1 + py_ecc/bls/api.py | 49 ++++++++++++++++++++++++++++++++++++++++++ py_ecc/bls/utils.py | 2 +- tests/test_bls.py | 37 +++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/py_ecc/bls/__init__.py b/py_ecc/bls/__init__.py index f52c3de1..0710cedb 100644 --- a/py_ecc/bls/__init__.py +++ b/py_ecc/bls/__init__.py @@ -5,4 +5,5 @@ sign, verify, verify_multiple, + verify_multiple_multiple, ) diff --git a/py_ecc/bls/api.py b/py_ecc/bls/api.py index 6a07d1d8..668465d5 100644 --- a/py_ecc/bls/api.py +++ b/py_ecc/bls/api.py @@ -1,5 +1,6 @@ from typing import ( Sequence, + Tuple, ) from eth_typing import ( @@ -31,6 +32,9 @@ pubkey_to_G1, signature_to_G2, ) +from secrets import ( + randbelow, +) def sign(message_hash: Hash32, @@ -109,3 +113,48 @@ def verify_multiple(pubkeys: Sequence[BLSPubkey], return final_exponentiation == FQ12.one() except (ValidationError, ValueError, AssertionError): return False + + +def verify_multiple_multiple( + signatures: Sequence[BLSSignature], + pubkeys_and_messages: Tuple[Sequence[BLSPubkey], Sequence[Hash32]], + domain: int)-> bool: + if len(signatures) != len(pubkeys_and_messages): + raise ValidationError( + "len(signatures) (%s) should be equal to len(pubkeys_and_messages) (%s)" % ( + len(signatures), len(pubkeys_and_messages) + ) + ) + try: + random_ints = tuple(2**randbelow(64) for _ in range(len(signatures))) + o = FQ12.one() + for index, pm in enumerate(pubkeys_and_messages): + pubkeys, message_hashes = pm # type: ignore + len_msgs = len(message_hashes) + if len(pubkeys) != len_msgs: + raise ValidationError( + "len(pubkeys) (%s) should be equal to len(message_hashes) (%s)" % ( + len(pubkeys), len_msgs + ) + ) + for m_pubs in set(message_hashes): + + # aggregate the pubs + group_pub = Z1 + for i in range(len_msgs): + if message_hashes[i] == m_pubs: + group_pub = add(group_pub, pubkey_to_G1(pubkeys[i])) + o *= pairing( + multiply(hash_to_G2(m_pubs, domain), random_ints[index]), + group_pub, + final_exponentiate=False, + ) + agg_sig = Z2 + for index, sig in enumerate(signatures): + agg_sig = add(agg_sig, multiply(signature_to_G2(sig), random_ints[index])) + o *= pairing(agg_sig, neg(G1), final_exponentiate=False) + + final_exponentiation = final_exponentiate(o) + return final_exponentiation == FQ12.one() + except (ValidationError, ValueError, AssertionError): + return False diff --git a/py_ecc/bls/utils.py b/py_ecc/bls/utils.py index e8ad1658..5da0b4b4 100644 --- a/py_ecc/bls/utils.py +++ b/py_ecc/bls/utils.py @@ -146,7 +146,7 @@ def G1_to_pubkey(pt: G1Uncompressed) -> BLSPubkey: def pubkey_to_G1(pubkey: BLSPubkey) -> G1Uncompressed: - z = big_endian_to_int(pubkey) + z = G1Compressed(big_endian_to_int(pubkey)) return decompress_G1(z) # diff --git a/tests/test_bls.py b/tests/test_bls.py index 94173736..aa24f159 100644 --- a/tests/test_bls.py +++ b/tests/test_bls.py @@ -10,6 +10,7 @@ sign, verify, verify_multiple, + verify_multiple_multiple, ) from py_ecc.bls.hash import ( hash_eth2, @@ -44,6 +45,7 @@ normalize, field_modulus as q, ) +from random import sample @pytest.mark.parametrize( @@ -238,3 +240,38 @@ def test_multi_aggregation(msg_1, msg_2, privkeys_1, privkeys_2): signature=aggsig, domain=domain, ) + + +def test_multi_multi(): + domain = 0 + validator_indices = tuple(range(10)) + privkeys = tuple(2**i for i in validator_indices) + pubkeys = [privtopub(k) for k in privkeys] + + class Attestation: + def __init__(self, msg_1, msg_2): + msg_1_validators = sample(validator_indices, 3) + msg_2_validators = sample(validator_indices, 3) + self.agg_pubkeys = [ + aggregate_pubkeys([pubkeys[i] for i in msg_1_validators]), + aggregate_pubkeys([pubkeys[i] for i in msg_2_validators]), + ] + self.msgs = [msg_1, msg_2] + msg_1_sigs = [sign(msg_1, privkeys[i], domain) for i in msg_1_validators] + msg_2_sigs = [sign(msg_2, privkeys[i], domain) for i in msg_2_validators] + self.sig = aggregate_signatures([ + aggregate_signatures(msg_1_sigs), + aggregate_signatures(msg_2_sigs), + ]) + + msgs = ( + (b'\x12' * 32, b'\x34' * 32), + (b'\x56' * 32, b'\x78' * 32), + (b'\x90' * 32, b'\xab' * 32), + ) + atts = [Attestation(msg_1, msg_2) for msg_1, msg_2 in msgs] + assert verify_multiple_multiple( + signatures=[att.sig for att in atts], + pubkeys_and_messages=[[att.agg_pubkeys, att.msgs] for att in atts], + domain=domain, + )