44 Tuple ,
55)
66
7+ from eth_utils import (
8+ big_endian_to_int ,
9+ )
710
8- from py_ecc .optimized_bn128 import ( # NOQA
11+ from py_ecc .optimized_bls12_381 import ( # NOQA
912 G1 ,
1013 G2 ,
1114 Z1 ,
1821 FQ12 ,
1922 pairing ,
2023 normalize ,
21- field_modulus ,
24+ field_modulus as q ,
2225 b ,
2326 b2 ,
2427 is_on_curve ,
2528 curve_order ,
2629 final_exponentiate
2730)
28- from eth .utils .blake import blake
31+ from eth_hash .auto import keccak as hash
32+
2933
34+ G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109 # noqa: E501
35+ qmod = q ** 2 - 1
36+ eighth_roots_of_unity = [
37+ FQ2 ([1 , 1 ]) ** ((qmod * k ) // 8 )
38+ for k in range (8 )
39+ ]
3040
31- CACHE = {} # type: Dict[bytes, Tuple[FQ2, FQ2, FQ2]]
32- # 16th root of unity
33- HEX_ROOT = FQ2 ([21573744529824266246521972077326577680729363968861965890554801909984373949499 ,
34- 16854739155576650954933913186877292401521110422362946064090026408937773542853 ])
3541
42+ #
43+ # Helpers
44+ #
45+ def modular_squareroot (value : int ) -> int :
46+ """
47+ ``modular_squareroot(x)`` returns the value ``y`` such that ``y**2 % q == x``,
48+ and None if this is not possible. In cases where there are two solutions,
49+ the value with higher imaginary component is favored;
50+ if both solutions have equal imaginary component the value with higher real
51+ component is favored.
52+ """
53+ candidate_squareroot = value ** ((qmod + 8 ) // 16 )
54+ check = candidate_squareroot ** 2 / value
55+ if check in eighth_roots_of_unity [::2 ]:
56+ x1 = candidate_squareroot / eighth_roots_of_unity [eighth_roots_of_unity .index (check ) // 2 ]
57+ x2 = FQ2 ([- x1 .coeffs [0 ], - x1 .coeffs [1 ]])
58+ # x2 = - x2
59+ return x1 if (x1 .coeffs [1 ], x1 .coeffs [0 ]) > (x2 .coeffs [1 ], x2 .coeffs [0 ]) else x2
60+ return None
61+
62+
63+ def hash_to_G2 (message : bytes , domain : int ) -> Tuple [FQ2 , FQ2 , FQ2 ]:
64+ domain_in_bytes = domain .to_bytes (8 , 'big' )
65+ x1 = big_endian_to_int (hash (domain_in_bytes + b'\x01 ' + message ))
66+ x2 = big_endian_to_int (hash (domain_in_bytes + b'\x02 ' + message ))
67+ x_coordinate = FQ2 ([x1 , x2 ]) # x1 + x2 * i
68+ while 1 :
69+ x_cubed_plus_b2 = x_coordinate ** 3 + FQ2 ([4 , 4 ])
70+ y_coordinate = modular_squareroot (x_cubed_plus_b2 )
71+ if y_coordinate is not None :
72+ break
73+ x_coordinate += FQ2 ([1 , 0 ]) # Add one until we get a quadratic residue
3674
37- assert HEX_ROOT ** 8 != FQ2 ([1 , 0 ])
38- assert HEX_ROOT ** 16 == FQ2 ([1 , 0 ])
75+ return multiply (
76+ (x_coordinate , y_coordinate , FQ2 ([1 , 0 ])),
77+ G2_cofactor
78+ )
3979
4080
81+ #
82+ # G1
83+ #
4184def compress_G1 (pt : Tuple [FQ2 , FQ2 , FQ2 ]) -> int :
4285 x , y = normalize (pt )
43- return x .n + 2 ** 255 * (y .n % 2 )
86+ return x .n + 2 ** 383 * (y .n % 2 )
4487
4588
4689def decompress_G1 (p : int ) -> Tuple [FQ , FQ , FQ ]:
4790 if p == 0 :
4891 return (FQ (1 ), FQ (1 ), FQ (0 ))
49- x = p % 2 ** 255
50- y_mod_2 = p // 2 ** 255
51- y = pow ((x ** 3 + b .n ) % field_modulus , (field_modulus + 1 ) // 4 , field_modulus )
52- assert pow (y , 2 , field_modulus ) == (x ** 3 + b .n ) % field_modulus
92+ x = p % 2 ** 383
93+ y_mod_2 = p // 2 ** 383
94+ y = pow ((x ** 3 + b .n ) % q , (q + 1 ) // 4 , q )
95+ assert pow (y , 2 , q ) == (x ** 3 + b .n ) % q
5396 if y % 2 != y_mod_2 :
54- y = field_modulus - y
97+ y = q - y
5598 return (FQ (x ), FQ (y ), FQ (1 ))
5699
57100
58- def sqrt_fq2 (x : FQ2 ) -> FQ2 :
59- y = x ** ((field_modulus ** 2 + 15 ) // 32 )
60- while y ** 2 != x :
61- y *= HEX_ROOT
62- return y
63-
64-
65- def hash_to_G2 (m : bytes ) -> Tuple [FQ2 , FQ2 , FQ2 ]:
66- """
67- WARNING: this function has not been standardized yet.
68- """
69- if m in CACHE :
70- return CACHE [m ]
71- k2 = m
72- while 1 :
73- k1 = blake (k2 )
74- k2 = blake (k1 )
75- x1 = int .from_bytes (k1 , 'big' ) % field_modulus
76- x2 = int .from_bytes (k2 , 'big' ) % field_modulus
77- x = FQ2 ([x1 , x2 ])
78- xcb = x ** 3 + b2
79- if xcb ** ((field_modulus ** 2 - 1 ) // 2 ) == FQ2 ([1 , 0 ]):
80- break
81- y = sqrt_fq2 (xcb )
82- o = multiply ((x , y , FQ2 ([1 , 0 ])), 2 * field_modulus - curve_order )
83- CACHE [m ] = o
84- return o
85-
86-
101+ #
102+ # G2
103+ #
87104def compress_G2 (pt : Tuple [FQ2 , FQ2 , FQ2 ]) -> Tuple [int , int ]:
88105 assert is_on_curve (pt , b2 )
89106 x , y = normalize (pt )
90- return (x .coeffs [0 ] + 2 ** 255 * (y .coeffs [0 ] % 2 ), x .coeffs [1 ])
107+ return (x .coeffs [0 ] + 2 ** 383 * (y .coeffs [0 ] % 2 ), x .coeffs [1 ])
91108
92109
93110def decompress_G2 (p : bytes ) -> Tuple [FQ2 , FQ2 , FQ2 ]:
94- x1 = p [0 ] % 2 ** 255
95- y1_mod_2 = p [0 ] // 2 ** 255
111+ x1 = p [0 ] % 2 ** 383
112+ y1_mod_2 = p [0 ] // 2 ** 383
96113 x2 = p [1 ]
97114 x = FQ2 ([x1 , x2 ])
98115 if x == FQ2 ([0 , 0 ]):
99116 return FQ2 ([1 , 0 ]), FQ2 ([1 , 0 ]), FQ2 ([0 , 0 ])
100- y = sqrt_fq2 (x ** 3 + b2 )
117+ y = modular_squareroot (x ** 3 + b2 )
101118 if y .coeffs [0 ] % 2 != y1_mod_2 :
102119 y = y * - 1
103- assert is_on_curve (( x , y , FQ2 ([ 1 , 0 ])), b2 )
120+
104121 return x , y , FQ2 ([1 , 0 ])
105122
106123
107- def sign (m : bytes , k : int ) -> Tuple [int , int ]:
108- return compress_G2 (multiply (hash_to_G2 (m ), k ))
124+ #
125+ # APIs
126+ #
127+ def sign (message : bytes ,
128+ privkey : int ,
129+ domain : int ) -> Tuple [int , int ]:
130+ return compress_G2 (
131+ multiply (
132+ hash_to_G2 (message , domain ),
133+ privkey
134+ )
135+ )
109136
110137
111138def privtopub (k : int ) -> int :
112139 return compress_G1 (multiply (G1 , k ))
113140
114141
115- def verify (m : bytes , pub : int , sig : bytes ) -> bool :
142+ def verify (m : bytes , pub : int , sig : bytes , domain : int ) -> bool :
116143 final_exponentiation = final_exponentiate (
117144 pairing (decompress_G2 (sig ), G1 , False ) *
118- pairing (hash_to_G2 (m ), neg (decompress_G1 (pub )), False )
145+ pairing (hash_to_G2 (m , domain ), neg (decompress_G1 (pub )), False )
119146 )
120147 return final_exponentiation == FQ12 .one ()
121148
@@ -132,3 +159,22 @@ def aggregate_pubs(pubs: Iterable[int]) -> int:
132159 for p in pubs :
133160 o = add (o , decompress_G1 (p ))
134161 return compress_G1 (o )
162+
163+
164+ def multi_verify (pubs , msgs , sig , domain ):
165+ len_msgs = len (msgs )
166+ assert len (pubs ) == len_msgs
167+
168+ o = FQ12 ([1 ] + [0 ] * 11 )
169+ for m in set (msgs ):
170+ # aggregate the pubs
171+ group_pub = Z1
172+ for i in range (len_msgs ):
173+ if msgs [i ] == m :
174+ group_pub = add (group_pub , decompress_G1 (pubs [i ]))
175+
176+ o *= pairing (hash_to_G2 (m , domain ), group_pub , False )
177+ o *= pairing (decompress_G2 (sig ), neg (G1 ), False )
178+
179+ final_exponentiation = final_exponentiate (o )
180+ return final_exponentiation == FQ12 .one ()
0 commit comments