ethereum / py_ecc

Python implementation of ECC pairing and bn_128 and bls12_381 curve operations
MIT License
183 stars 82 forks source link

Test failures on Python 3.12 #133

Open dotlambda opened 5 months ago

dotlambda commented 5 months ago

What happened?

When running pytest on Python 3.12 a bunch of tests fail with

RecursionError: maximum recursion depth exceeded

which seems to stem from

AttributeError("'bls12_381_FQ12' object has no attribute 'coeffs'") raised in repr()

Code that produced the error

No response

Full error output

No response

Fill this section in if you know how this could or should be fixed

No response

py-ecc Version

7.0.0

Python Version

3.12.2

Operating System

Linux

Output from pip freeze

No response

cre-mer commented 1 month ago

I have the same issue while pairing two points using py_ecc.bn128 with the following setup:

py-ecc Version

7.0.1

Python Version

3.12.0

Operating System

macOS Sonoma 14.5, Apple M1 Pro

When switching to python@3.10 it worked fine

pacrob commented 4 weeks ago

I've just passed all tests locally with py==3.12.3 and py_ecc==7.0.1 on Linux. Could you post the code that's giving the error?

jimmychu0807 commented 1 week ago

@pacrob this is my code:

from py_ecc.bn128 import G1, G2, pairing, multiply

pairing(G2, G1)

Python version: 3.12.2
py-ecc version: 7.0.1

This is the whole error trace ``` --------------------------------------------------------------------------- RecursionError Traceback (most recent call last) Cell In[16], line 13 10 C = multiply(G2, 5 * 6) 11 # D = multiply(G1, 5 * 6) ---> 13 pairing(G2, G1) File [~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/bn128/bn128_pairing.py:106](http://localhost:8888/lab/tree/~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/bn128/bn128_pairing.py#line=105), in pairing(Q, P) 104 assert is_on_curve(Q, b2) 105 assert is_on_curve(P, b) --> 106 return miller_loop(twist(Q), cast_point_to_fq12(P)) File [~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/bn128/bn128_pairing.py:99](http://localhost:8888/lab/tree/~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/bn128/bn128_pairing.py#line=98), in miller_loop(Q, P) 97 f = f * linefunc(R, nQ2, P) 98 # R = add(R, nQ2) This line is in many specifications but technically does nothing ---> 99 return f ** ((field_modulus**12 - 1) // curve_order) File ~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py:276, in FQP.__pow__(self, other) 274 return type(self)(self.coeffs) 275 elif other % 2 == 0: --> 276 return (self * self) ** (other // 2) 277 else: 278 return ((self * self) ** int(other // 2)) * self File [~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py:276](http://localhost:8888/lab/tree/~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py#line=275), in FQP.__pow__(self, other) 274 return type(self)(self.coeffs) 275 elif other % 2 == 0: --> 276 return (self * self) ** (other // 2) 277 else: 278 return ((self * self) ** int(other // 2)) * self [... skipping similar frames: FQP.__pow__ at line 276 (3 times)] File [~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py:278](http://localhost:8888/lab/tree/~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py#line=277), in FQP.__pow__(self, other) 276 return (self * self) ** (other // 2) 277 else: --> 278 return ((self * self) ** int(other // 2)) * self [... skipping similar frames: FQP.__pow__ at line 276 (2 times)] File [~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py:278](http://localhost:8888/lab/tree/~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py#line=277), in FQP.__pow__(self, other) 276 return (self * self) ** (other // 2) 277 else: --> 278 return ((self * self) ** int(other // 2)) * self [... skipping similar frames: FQP.__pow__ at line 276 (368 times), FQP.__pow__ at line 278 (359 times)] File [~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py:278](http://localhost:8888/lab/tree/~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py#line=277), in FQP.__pow__(self, other) 276 return (self * self) ** (other // 2) 277 else: --> 278 return ((self * self) ** int(other // 2)) * self File [~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py:276](http://localhost:8888/lab/tree/~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py#line=275), in FQP.__pow__(self, other) 274 return type(self)(self.coeffs) 275 elif other % 2 == 0: --> 276 return (self * self) ** (other // 2) 277 else: 278 return ((self * self) ** int(other // 2)) * self File [~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py:246](http://localhost:8888/lab/tree/~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py#line=245), in FQP.__mul__(self, other) 242 for i in range(self.degree): 243 b[exp + i] -= top * self.FQP_corresponding_FQ_class( 244 self.modulus_coeffs[i] 245 ) --> 246 return type(self)(b) 247 else: 248 raise TypeError( 249 "Expected an int or FQ object or FQP object, " 250 f"but got object of type {type(other)}" 251 ) File [~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py:374](http://localhost:8888/lab/tree/~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py#line=373), in FQ12.__init__(self, coeffs) 371 if self.FQ12_MODULUS_COEFFS is None: 372 raise AttributeError("FQ12 Modulus Coeffs haven't been specified") --> 374 super().__init__(coeffs, self.FQ12_MODULUS_COEFFS) File [~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py:208](http://localhost:8888/lab/tree/~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py#line=207), in FQP.__init__(self, coeffs, modulus_coeffs) 204 # Encoding all coefficients in the corresponding type FQ 205 self.FQP_corresponding_FQ_class = type( 206 "FQP_corresponding_FQ_class", (FQ,), {"field_modulus": self.field_modulus} 207 ) # type: Type[FQ] --> 208 self.coeffs = tuple( 209 self.FQP_corresponding_FQ_class(c) for c in coeffs 210 ) # type: Tuple[IntOrFQ, ...] 211 # The coefficients of the modulus, without the leading [1] 212 self.modulus_coeffs = tuple(modulus_coeffs) # type: Tuple[IntOrFQ, ...] File [~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py:209](http://localhost:8888/lab/tree/~/.pyenv/versions/miniconda3-latest/lib/python3.12/site-packages/py_ecc/fields/field_elements.py#line=208), in (.0) 204 # Encoding all coefficients in the corresponding type FQ 205 self.FQP_corresponding_FQ_class = type( 206 "FQP_corresponding_FQ_class", (FQ,), {"field_modulus": self.field_modulus} 207 ) # type: Type[FQ] 208 self.coeffs = tuple( --> 209 self.FQP_corresponding_FQ_class(c) for c in coeffs 210 ) # type: Tuple[IntOrFQ, ...] 211 # The coefficients of the modulus, without the leading [1] 212 self.modulus_coeffs = tuple(modulus_coeffs) # type: Tuple[IntOrFQ, ...] RecursionError: maximum recursion depth exceeded ```
cre-mer commented 1 week ago

@pacrob, same as @jimmychu0807:

from py_ecc.bn128 import G1, G2, pairing

pairing(G2, G1)

Please, refer to this comment for additional system information.

pacrob commented 1 week ago

I've tried it locally with py3.12.3, 3.12.2, and 3.12.1, and had a colleague check with 3.12.3 on a mac M3, everything working fine.

In [1]: from py_ecc.bn128 import G1, G2, pairing
   ...: 
   ...: pairing(G2, G1)
Out[1]: (18443897754565973717256850119554731228214108935025491924036055734000366132575, 10734401203193558706037776473742910696504851986739882094082017010340198538454, 5985796159921227033560968606339653189163760772067273492369082490994528765680, 4093294155816392700623820137842432921872230622290337094591654151434545306688, 642121370160833232766181493494955044074321385528883791668868426879070103434, 4527449849947601357037044178952942489926487071653896435602814872334098625391, 3758435817766288188804561253838670030762970764366672594784247447067868088068, 18059168546148152671857026372711724379319778306792011146784665080987064164612, 14656606573936501743457633041048024656612227301473084805627390748872617280984, 17918828665069491344039743589118342552553375221610735811112289083834142789347, 19455424343576886430889849773367397946457449073528455097210946839000147698372, 7484542354754424633621663080190936924481536615300815203692506276894207018007)

In [2]: import sys

In [3]: print(sys.getrecursionlimit())
100000

What's your recursion limit set to? I can actually replicate if I set my recursion limit to 2500, works again at 3000. Maybe the default was higher in your 3.10 install than in your 3.12?

cre-mer commented 1 week ago

My default recursion limit is 1000. I changed it to very high numbers to rule out the possibility, but it still fails with the same error.

jimmychu0807 commented 1 week ago

@pacrob sigh, mystery of life 🤷🏻 ...

This is what I get when running in Python 3.10

Screenshot 2024-07-18 at 12 39 55

This is what I get when running in Python 3.12

Screenshot 2024-07-18 at 12 39 03