Skip to content

Commit

Permalink
🔥 pure python zk-ecip
Browse files Browse the repository at this point in the history
  • Loading branch information
feltroidprime committed Jun 29, 2024
1 parent 7d4dc79 commit f54debd
Show file tree
Hide file tree
Showing 13 changed files with 228,106 additions and 227,621 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ Ensure you have the following installed:
- [graphviz](https://graphviz.org/download/) - Necessary for generating graphical representations of profiling data.
- A functional [SageMath](https://www.sagemath.org/download.html) installation or an operational [Docker](https://www.docker.com/get-started/) daemon with non-sudo privileges.

If you're using SageMath directly, make sure the edit the default parameter `use_docker` to `False` in the `EcipCLI` class inside `tools/ecip_cli.py`

### Setup

Once you have the prerequisites installed, clone the repository, and set up your development environment with the following command. Be sure to run this command from the root of the repository.
Expand Down
118 changes: 102 additions & 16 deletions hydra/algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,27 @@ def __init__(
def __repr__(self) -> str:
return f"Polynomial({[x.value for x in self.get_coeffs()]})"

def print_as_sage_poly(self, var_name: str = "z") -> str:
def print_as_sage_poly(self, var_name: str = "z", as_hex: bool = False) -> str:
"""
Prints the polynomial ready to be used in SageMath.
"""
if self.is_zero():
return ""
coeffs = self.get_value_coeffs()
string = ""
for i, coeff in enumerate(coeffs[::-1]):
string += f"{hex(coeff)}*{var_name}^{len(coeffs) - 1 - i} + "
return string[:-3]
if coeff == 0:
continue
else:
coeff_str = hex(coeff) if as_hex else str(coeff)
if i == len(coeffs) - 1:

string += f"{coeff_str}"
elif i == len(coeffs) - 2:
string += f"{coeff_str}*{var_name} + "
else:
string += f"{coeff_str}*{var_name}^{len(coeffs) - 1 - i} + "
return string

def __getitem__(self, i: int) -> PyFelt:
try:
Expand Down Expand Up @@ -205,17 +217,36 @@ def get_coeffs(self) -> list[PyFelt]:
def get_value_coeffs(self) -> list[int]:
return [c.value for c in self.get_coeffs()]

def __add__(self, other: "Polynomial") -> "Polynomial":
if self.degree() == -1:
return other
elif other.degree() == -1:
return self
field = self.field
coeffs = [field.zero()] * max(len(self.coefficients), len(other.coefficients))
for i in range(len(self.coefficients)):
coeffs[i] = coeffs[i] + self.coefficients[i]
for i in range(len(other.coefficients)):
coeffs[i] = coeffs[i] + other.coefficients[i]
def differentiate(self) -> "Polynomial":
"""
Differentiates the polynomial with respect to x.
Returns:
Polynomial: The derivative of the polynomial.
"""
if len(self.coefficients) <= 1:
return Polynomial([self.field.zero()])

derivative_coeffs = [
self.coefficients[i] * PyFelt(i, self.p)
for i in range(1, len(self.coefficients))
]
return Polynomial(derivative_coeffs)

def __add__(self, other: Polynomial) -> Polynomial:
if not isinstance(other, Polynomial):
raise TypeError(f"Cannot add Polynomial and {type(other)}")

ns, no = len(self.coefficients), len(other.coefficients)
if ns >= no:
coeffs = self.coefficients[:]
for i in range(no):
coeffs[i] = coeffs[i] + other.coefficients[i]
else:
coeffs = other.coefficients[:]
for i in range(ns):
coeffs[i] = coeffs[i] + self.coefficients[i]

return Polynomial(coeffs)

def __neg__(self) -> "Polynomial":
Expand Down Expand Up @@ -301,7 +332,7 @@ def __eq__(self, other: object) -> bool:
if self.degree() == -1:
return True

return self.coefficients == other.coefficients
return self.get_coeffs() == other.get_coeffs()

def __neq__(self, other: object) -> bool:
return not self.__eq__(other)
Expand All @@ -317,6 +348,14 @@ def is_zero(self) -> bool:
return False
return True

@staticmethod
def zero(p: int) -> "Polynomial":
return Polynomial([PyFelt(0, p)])

@staticmethod
def one(p: int) -> "Polynomial":
return Polynomial([PyFelt(1, p)])

def evaluate(self, point: PyFelt) -> PyFelt:
xi = self.field.one()
value = self.field.zero()
Expand Down Expand Up @@ -395,7 +434,7 @@ def xgcd(x: Polynomial, y: Polynomial) -> tuple[Polynomial, Polynomial, Polynomi
old_s, s = (s, old_s - quotient * s)
old_t, t = (t, old_t - quotient * t)

lcinv = old_r.coefficients[old_r.degree()].__inv__()
lcinv = old_r.leading_coefficient().__inv__()

# a, b, g
return (
Expand Down Expand Up @@ -445,6 +484,34 @@ class RationalFunction:
numerator: Polynomial
denominator: Polynomial

@property
def field(self) -> BaseField:
return self.numerator.field

def simplify(self) -> "RationalFunction":
_, _, gcd = Polynomial.xgcd(self.numerator, self.denominator)
num_simplified = self.numerator // gcd
den_simplified = self.denominator // gcd
return RationalFunction(
num_simplified * self.denominator.leading_coefficient().__inv__(),
den_simplified * den_simplified.leading_coefficient().__inv__(),
)

def __add__(self, other: "RationalFunction") -> "RationalFunction":
return RationalFunction(
self.numerator * other.denominator + other.numerator * self.denominator,
self.denominator * other.denominator,
).simplify()

def __mul__(self, other: int | PyFelt) -> "RationalFunction":
if isinstance(other, int):
other = self.field(other)
elif isinstance(other, PyFelt):
other = self.field(other.value)
else:
raise TypeError(f"Cannot multiply RationalFunction with {type(other)}")
return RationalFunction(self.numerator * other, self.denominator)

def evaluate(self, x: PyFelt) -> PyFelt:
return self.numerator.evaluate(x) / self.denominator.evaluate(x)

Expand All @@ -461,6 +528,22 @@ class FunctionFelt:
a: RationalFunction
b: RationalFunction

@property
def field(self) -> BaseField:
return self.a.numerator.field

def simplify(self) -> "FunctionFelt":
return FunctionFelt(self.a.simplify(), self.b.simplify())

def __add__(self, other: "FunctionFelt") -> "FunctionFelt":
return FunctionFelt(self.a + other.a, self.b + other.b)

def __mul__(self, other: PyFelt | int) -> "FunctionFelt":
return FunctionFelt(self.a * other, self.b * other)

def __rmul__(self, other: PyFelt | int) -> "FunctionFelt":
return self.__mul__(other)

def evaluate(self, x: PyFelt, y: PyFelt) -> PyFelt:
return self.a.evaluate(x) + y * self.b.evaluate(x)

Expand All @@ -470,6 +553,9 @@ def degrees_infos(self) -> dict[str, dict[str, int]]:
"b": self.b.degrees_infos(),
}

def print_as_sage_poly(self, var: str = "x") -> str:
return f"(({self.b.numerator.print_as_sage_poly(var)}) / ({self.b.denominator.print_as_sage_poly(var)}) * y + ({self.a.numerator.print_as_sage_poly(var)} / ({self.a.denominator.print_as_sage_poly(var)})"


if __name__ == "__main__":
p = 10000000007
Expand Down
25 changes: 19 additions & 6 deletions hydra/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,14 @@ def __eq__(self, other: "G1Point") -> bool:
)

def __post_init__(self):
if self.is_infinity():
return
if not self.is_on_curve():
raise ValueError(f"Point {self} is not on the curve")

def is_infinity(self) -> bool:
return self.x == 0 and self.y == 0

def to_cairo_1(self) -> str:
return f"G1Point{{x: {int_to_u384(self.x)}, y: {int_to_u384(self.y)}}};"

Expand Down Expand Up @@ -360,17 +365,25 @@ def msm(points: list["G1Point"], scalars: list[int]) -> "G1Point":
return scalar_mul

def scalar_mul(self, scalar: int) -> "G1Point":
if self.is_infinity():
return self
if scalar < 0:
return -self.scalar_mul(-scalar)
res = ec_safe_mult(
scalar,
(self.x, self.y),
CURVES[self.curve_id.value].a,
CURVES[self.curve_id.value].p,
)
if res == EcInfinity:
return res
return G1Point(0, 0, self.curve_id)
return G1Point(res[0], res[1], self.curve_id)

def add(self, other: "G1Point") -> "G1Point":
if self.is_infinity():
return other
if other.is_infinity():
return self
if self.curve_id != other.curve_id:
raise ValueError("Points are not on the same curve")
res = ec_safe_add(
Expand All @@ -379,12 +392,12 @@ def add(self, other: "G1Point") -> "G1Point":
CURVES[self.curve_id.value].a,
CURVES[self.curve_id.value].p,
)
if res == EcInfinity:
return res
if isinstance(res, EcInfinity):
return G1Point(0, 0, self.curve_id)
return G1Point(res[0], res[1], self.curve_id)

def __neg__(self) -> "G1Point":
return G1Point(self.x, -self.y, self.curve_id)
return G1Point(self.x, -self.y % CURVES[self.curve_id.value].p, self.curve_id)


@dataclass(frozen=True)
Expand Down Expand Up @@ -666,14 +679,14 @@ def generate_frobenius_maps(
for i in range(extension_degree):
for f_index, poly in enumerate(V_pow):
if poly[i] != 0:
hex_value = f"0x{poly[i]:x}"
hex_value = f"0x{poly[i].value:x}"
compact_hex = (
f"{hex_value[:6]}...{hex_value[-4:]}"
if len(hex_value) > 10
else hex_value
)
k_expressions[i] += f" + {compact_hex} * f_{f_index}"
constants_list[i].append((f_index, poly[i]))
constants_list[i].append((f_index, poly[i].value))
return k_expressions, constants_list


Expand Down
Loading

0 comments on commit f54debd

Please sign in to comment.