Cython for Miller's Loop Cryptography first introduced, which dramatically reduces verification times. Includes a setup.py option for manual builds if desired. README for manual compile if desired. And a fallback import using the legacy python-only system.

This commit is contained in:
SimplifiedPrivacy 2026-08-22 20:48:22 -04:00
parent a69b87817c
commit 39efcaa174
8 changed files with 192 additions and 8 deletions

6
.gitignore vendored
View file

@ -4,5 +4,11 @@ prototype_client.py
.idea
.venv
__pycache__
__pycache__/
dist
.mypy_cache
*.c
*.so
*.o
*.egg-info/
core/services/crypto/cython/build

View file

@ -1,5 +1,10 @@
# Major Change Log:
# Cython Introduced
### Aug 22, 2026
Cython for Miller's Loop Cryptography first introduced, which dramatically reduces verification times. Includes a setup.py option for manual builds if desired. README for manual compile if desired. And a fallback import using the legacy python-only system.
<br/>
# Vless Introduced
### Aug 20, 2026
Vless is now working. Config Parsing and setup is stable, and increased the wait time for connection testing. But this is a temporary solution. The real answer is not a static time check, but a dynamic result reading for when to test. Which then has it's own timeout time

View file

@ -15,13 +15,19 @@ from py_ecc.optimized_bls12_381 import (
)
from py_ecc.optimized_bls12_381.optimized_curve import FQ, FQ2
# for the (optional) validity tests:
from py_ecc.optimized_bls12_381 import pairing
# errors:
from core.errors.logger import logger
import traceback
# for the (optional) validity tests:
try:
from core.services.crypto.cython.bls12_381_pairing import pairing
logger.info("Imported Cython pairings")
except ImportError:
# Fallback to py_ecc if not compiled
from py_ecc.optimized_bls12_381 import pairing
logger.error("FAILED to import cython pairings, used python py_ecc")
class TicketCustomer:
"""
@ -206,6 +212,7 @@ class TicketCustomer:
blinded_signature = self._deserialize_point_g2(blind_signature)
blinded_commitment_as_dict = get_data(which_ticket, "blinded_commitment_json")
blinded_commitment = self._deserialize_point_g2(blinded_commitment_as_dict)
projective_public_key = self.load_key(string_public_key)
@ -214,6 +221,7 @@ class TicketCustomer:
return {"valid": False, "message": "invalid_key"}
# All of that was to prep the values for this pairing equation,
# Which now uses Cython
try:
if pairing(blinded_signature, G1) == pairing(
blinded_commitment, projective_public_key

View file

@ -0,0 +1,22 @@
# Overview
We use Cython to compile certain cryptographic operations to C, to make it faster. This is the situation with miller's loop from py_ecc.
# Pre-reqs:
In the same venv as the project itself:
```bash
pip install cython
pip install setuptools
```
<br/>
# Setup Compile
```bash
python3 setup.py build_ext --inplace
```
<br/>
# If any issues
Try the exact path to your venv.
```bash
path/to/your/venv/python3 setup.py build_ext --inplace
```

View file

@ -0,0 +1,125 @@
from py_ecc.fields import (
bls12_381_FQ as FQ,
bls12_381_FQ2 as FQ2,
bls12_381_FQ12 as FQ12
)
from py_ecc.fields.field_properties import (
field_properties,
)
from py_ecc.typing import (
Field,
Point2D,
)
from py_ecc.bls12_381 import (
G1,
add,
b,
b2,
curve_order,
double,
is_on_curve,
multiply,
twist,
)
field_modulus = field_properties["bls12_381"]["field_modulus"]
ate_loop_count = 15132376222941642752
log_ate_loop_count = 62
from typing import Optional, Tuple, Union
from py_ecc.fields import bls12_381_FQ as FQ_py_ecc
from py_ecc.utils import prime_field_inv
def linefunc(
P1: Tuple[Field, Field],
P2: Tuple[Field, Field],
T: Tuple[Field, Field]
) -> Field:
"""
Create a function representing the line between P1 and P2,
and evaluate it at T
"""
if P1 is None or P2 is None or T is None:
raise ValueError("Invalid input - no points-at-infinity allowed")
x1, y1 = P1
x2, y2 = P2
xt, yt = T
if x1 != x2:
m = (y2 - y1) / (x2 - x1)
return m * (xt - x1) - (yt - y1)
elif y1 == y2:
m = 3 * x1**2 / (2 * y1)
return m * (xt - x1) - (yt - y1)
else:
return xt - x1
def cast_point_to_fq12(
pt: Optional[Tuple[FQ, FQ]]
) -> Optional[Tuple[FQ12, FQ12]]:
if pt is None:
return None
x, y = pt
return (FQ12([x.n] + [0] * 11), FQ12([y.n] + [0] * 11))
# Check consistency of the "line function"
one = G1
two = double(G1)
three = multiply(G1, 3)
negone = multiply(G1, curve_order - 1)
negtwo = multiply(G1, curve_order - 2)
negthree = multiply(G1, curve_order - 3)
conditions = [
linefunc(one, two, one) == FQ(0),
linefunc(one, two, two) == FQ(0),
linefunc(one, two, three) != FQ(0),
linefunc(one, two, negthree) == FQ(0),
linefunc(one, negone, one) == FQ(0),
linefunc(one, negone, negone) == FQ(0),
linefunc(one, negone, two) != FQ(0),
linefunc(one, one, one) == FQ(0),
linefunc(one, one, two) != FQ(0),
linefunc(one, one, negtwo) == FQ(0),
]
if not all(conditions):
raise ValueError("Line function is inconsistent")
def miller_loop(Q: Point2D[FQ12], P: Point2D[FQ12]) -> FQ12:
cdef int i
if Q is None or P is None:
return FQ12.one()
R = Q
f = FQ12.one()
for i in range(log_ate_loop_count, -1, -1):
f = f * f * linefunc(R, R, P)
R = double(R)
if ate_loop_count & (2**i):
f = f * linefunc(R, Q, P)
R = add(R, Q)
return f ** ((field_modulus**12 - 1) // curve_order)
def pairing(Q: Point2D[FQ2], P: Point2D[FQ]) -> FQ12:
if not is_on_curve(Q, b2):
raise ValueError("Invalid input - point Q is not on the correct curve")
if not is_on_curve(P, b):
raise ValueError("Invalid input - point P is not on the correct curves")
return miller_loop(twist(Q), cast_point_to_fq12(P))
def final_exponentiate(p: FQ12) -> FQ12:
return p ** ((field_modulus**12 - 1) // curve_order)

View file

@ -0,0 +1,19 @@
"""
This is the Manual Setup for Cython extensions.
Compile with:
pip install cython setuptools
python3 setup.py build_ext --inplace
& Put the path to venv prior to the python3 if any issues.
path/to/venv/python3 setup.py build_ext --inplace
"""
from Cython.Build import cythonize
from setuptools import setup
setup(
ext_modules=cythonize("bls12_381_pairing.pyx", language_level=3)
)

View file

@ -25,7 +25,7 @@ def make_ONE_commitment_pair(
# Then BLIND it, so it can be sent to the billing server:
blind_commitment = profile_object.blind_commitment(which_ticket)
logger.info(f"blind_commitment is {blind_commitment}")
# logger.info(f"blind_commitment is {blind_commitment}")
# we need to make sure we actually saved the data,
# because it's the only way to unblind it later:

View file

@ -134,14 +134,10 @@ def validate_blind_signatures(
which_ticket, blind_signature, public_key
)
# bump loop for next round:
which_ticket = which_ticket + 1
if validity_data["valid"] == True:
# verification went fine:
notification = f"Verified {which_ticket}'s blind signature"
ticket_observer.notify("preparing", subject=notification)
continue
else:
# is it an invalid pub key?
if validity_data["message"] == "invalid_key":
@ -154,6 +150,9 @@ def validate_blind_signatures(
notification = f"Invalid {which_ticket} blind signature"
ticket_observer.notify("preparing", subject=notification)
# bump loop for next round:
which_ticket = which_ticket + 1
### LOOP DONE
logger.debug(
f"Exiting the validation loop with the list_of_failed_verifications: {list_of_failed_verifications}"