Skip to content

Commit cd57296

Browse files
fast-verification-of-multiple-bls-signatures
1 parent 2088796 commit cd57296

File tree

4 files changed

+88
-1
lines changed

4 files changed

+88
-1
lines changed

py_ecc/bls/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
sign,
66
verify,
77
verify_multiple,
8+
verify_multiple_multiple,
89
)

py_ecc/bls/api.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import (
22
Sequence,
3+
Tuple,
34
)
45

56
from eth_typing import (
@@ -31,6 +32,9 @@
3132
pubkey_to_G1,
3233
signature_to_G2,
3334
)
35+
from secrets import (
36+
randbelow,
37+
)
3438

3539

3640
def sign(message_hash: Hash32,
@@ -109,3 +113,48 @@ def verify_multiple(pubkeys: Sequence[BLSPubkey],
109113
return final_exponentiation == FQ12.one()
110114
except (ValidationError, ValueError, AssertionError):
111115
return False
116+
117+
118+
def verify_multiple_multiple(
119+
signatures: Sequence[BLSSignature],
120+
pubkeys_and_messages: Tuple[Sequence[BLSPubkey], Sequence[Hash32]],
121+
domain: int)-> bool:
122+
if len(signatures) != len(pubkeys_and_messages):
123+
raise ValidationError(
124+
"len(signatures) (%s) should be equal to len(pubkeys_and_messages) (%s)" % (
125+
len(signatures), len(pubkeys_and_messages)
126+
)
127+
)
128+
try:
129+
random_ints = tuple(2**randbelow(64) for _ in range(len(signatures)))
130+
o = FQ12.one()
131+
for index, pm in enumerate(pubkeys_and_messages):
132+
pubkeys, message_hashes = pm # type: ignore
133+
len_msgs = len(message_hashes)
134+
if len(pubkeys) != len_msgs:
135+
raise ValidationError(
136+
"len(pubkeys) (%s) should be equal to len(message_hashes) (%s)" % (
137+
len(pubkeys), len_msgs
138+
)
139+
)
140+
for m_pubs in set(message_hashes):
141+
142+
# aggregate the pubs
143+
group_pub = Z1
144+
for i in range(len_msgs):
145+
if message_hashes[i] == m_pubs:
146+
group_pub = add(group_pub, pubkey_to_G1(pubkeys[i]))
147+
o *= pairing(
148+
multiply(hash_to_G2(m_pubs, domain), random_ints[index]),
149+
group_pub,
150+
final_exponentiate=False,
151+
)
152+
agg_sig = Z2
153+
for index, sig in enumerate(signatures):
154+
agg_sig = add(agg_sig, multiply(signature_to_G2(sig), random_ints[index]))
155+
o *= pairing(agg_sig, neg(G1), final_exponentiate=False)
156+
157+
final_exponentiation = final_exponentiate(o)
158+
return final_exponentiation == FQ12.one()
159+
except (ValidationError, ValueError, AssertionError):
160+
return False

py_ecc/bls/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def G1_to_pubkey(pt: G1Uncompressed) -> BLSPubkey:
146146

147147

148148
def pubkey_to_G1(pubkey: BLSPubkey) -> G1Uncompressed:
149-
z = big_endian_to_int(pubkey)
149+
z = G1Compressed(big_endian_to_int(pubkey))
150150
return decompress_G1(z)
151151

152152
#

tests/test_bls.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
sign,
1111
verify,
1212
verify_multiple,
13+
verify_multiple_multiple,
1314
)
1415
from py_ecc.bls.hash import (
1516
hash_eth2,
@@ -44,6 +45,7 @@
4445
normalize,
4546
field_modulus as q,
4647
)
48+
from random import sample
4749

4850

4951
@pytest.mark.parametrize(
@@ -238,3 +240,38 @@ def test_multi_aggregation(msg_1, msg_2, privkeys_1, privkeys_2):
238240
signature=aggsig,
239241
domain=domain,
240242
)
243+
244+
245+
def test_multi_multi():
246+
domain = 0
247+
validator_indices = tuple(range(10))
248+
privkeys = tuple(2**i for i in validator_indices)
249+
pubkeys = [privtopub(k) for k in privkeys]
250+
251+
class Attestation:
252+
def __init__(self, msg_1, msg_2):
253+
msg_1_validators = sample(validator_indices, 3)
254+
msg_2_validators = sample(validator_indices, 3)
255+
self.agg_pubkeys = [
256+
aggregate_pubkeys([pubkeys[i] for i in msg_1_validators]),
257+
aggregate_pubkeys([pubkeys[i] for i in msg_2_validators]),
258+
]
259+
self.msgs = [msg_1, msg_2]
260+
msg_1_sigs = [sign(msg_1, privkeys[i], domain) for i in msg_1_validators]
261+
msg_2_sigs = [sign(msg_2, privkeys[i], domain) for i in msg_2_validators]
262+
self.sig = aggregate_signatures([
263+
aggregate_signatures(msg_1_sigs),
264+
aggregate_signatures(msg_2_sigs),
265+
])
266+
267+
msgs = (
268+
(b'\x12' * 32, b'\x34' * 32),
269+
(b'\x56' * 32, b'\x78' * 32),
270+
(b'\x90' * 32, b'\xab' * 32),
271+
)
272+
atts = [Attestation(msg_1, msg_2) for msg_1, msg_2 in msgs]
273+
assert verify_multiple_multiple(
274+
signatures=[att.sig for att in atts],
275+
pubkeys_and_messages=[[att.agg_pubkeys, att.msgs] for att in atts],
276+
domain=domain,
277+
)

0 commit comments

Comments
 (0)