Skip to content

Commit

Permalink
bump cairo to 2.8.0 & add comments in hydra (#178)
Browse files Browse the repository at this point in the history
  • Loading branch information
feltroidprime authored Aug 30, 2024
1 parent a08af61 commit 3781978
Show file tree
Hide file tree
Showing 36 changed files with 243 additions and 431 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cairo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@v3
- uses: software-mansion/setup-scarb@v1
with:
scarb-version: "2.7.1"
scarb-version: "2.8.0"
- run: scarb fmt --check
working-directory: src/
- run: cd src/ && scarb test
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
- name: Setup Scarb
uses: software-mansion/setup-scarb@v1
with:
scarb-version: "2.7.1"
scarb-version: "2.8.0"
- name: Install dependencies
run: make setup

Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ tools/make/requirements.txt

src/cairo/target/
*target*

Scarb.lock
tests/contracts_e2e/devnet/*

!hydra/garaga/starknet/groth16_contract_generator/examples/*.json
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ steps:
ci-e2e:
./tools/make/ci_e2e.sh

ci-hydra:
./tools/make/ci_hydra.sh

ci-cairo:
./tools/make/ci_cairo.sh

clean:
rm -rf build/compiled_cairo_files
mkdir -p build
mkdir build/compiled_cairo_files

hints:
./tools/make/gen_hints_document.py
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ To get started with Garaga, you'll need to have some tools and dependencies inst

Ensure you have the following installed:
- [Python 3.10](https://www.python.org/downloads/) - /!\ Make sure `python3.10` is a valid command in your terminal. The core language used for development. Make sure you have the correct dependencies installed (in particular, GMP) for the `fastecdsa` python package. See [here](https://pypi.org/project/fastecdsa/#installing) for linux and [here](https://github.com/AntonKueltz/fastecdsa/issues/74) for macos.
- [Scarb 2.7.1](https://docs.swmansion.com/scarb/download.html) - The Cairo package manager. Comes with Cairo inside. Requires [Rust](https://www.rust-lang.org/tools/install).
- [Scarb 2.8.0](https://docs.swmansion.com/scarb/download.html) - The Cairo package manager. Comes with Cairo inside. Requires [Rust](https://www.rust-lang.org/tools/install).

##### Optionally :

Expand Down
2 changes: 1 addition & 1 deletion docs/gitbook/installation/developer-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ icon: wrench
To work with Garaga, you need the following dependencies : 

* Python 3.10. The command `python3.10` should be available and working in your terminal. 
* [Scarb](https://docs.swmansion.com/scarb/download.html) v2.7.1. 
* [Scarb](https://docs.swmansion.com/scarb/download.html) v2.8.0. 
* [Rust](https://www.rust-lang.org/tools/install)

Simply clone the [repository](https://github.com/keep-starknet-strange/garaga) :
Expand Down
50 changes: 46 additions & 4 deletions hydra/garaga/hints/ecip.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@
from garaga.poseidon_transcript import hades_permutation


def get_field_type_from_ec_point(P) -> type[T]:
def get_field_type_from_ec_point(P: G1Point | G2Point) -> type[T]:
"""
Maps an elliptic curve point to the type of the field it belongs to.
G1Point -> PyFelt
G2Point -> Fp2
"""
if isinstance(P, G1Point):
return PyFelt
elif isinstance(P, G2Point):
Expand All @@ -23,7 +28,12 @@ def get_field_type_from_ec_point(P) -> type[T]:
raise ValueError(f"Invalid point type {type(P)}")


def get_ec_group_class_from_ec_point(P):
def get_ec_group_class_from_ec_point(P: G1Point | G2Point) -> type[G1Point | G2Point]:
"""
Maps an elliptic curve point to the class of the elliptic curve group it belongs to.
G1Point -> G1Point
G2Point -> G2Point
"""
if isinstance(P, G1Point):
return G1Point
elif isinstance(P, G2Point):
Expand All @@ -35,11 +45,33 @@ def get_ec_group_class_from_ec_point(P):
def derive_ec_point_from_X(
x: PyFelt | int | Fp2, curve_id: CurveID
) -> tuple[PyFelt, PyFelt, list[PyFelt]] | tuple[Fp2, Fp2, list[Fp2]]:
"""
From a "random" x coordinate (in practice obtained via the Cairo Poseidon252 hash), finds via a
"try-and-increment" algorithm a point on the curve for a given curveID.
Works for curves over base field ("PyFelt") or degree 2 extension field ("Fp2")
Returns :
- x (PyFelt or Fp2) - x coordinate of the obtained point after the try-and-increment
- y (PyFelt or Fp2) - y coordinate of the obtained point after the try-and-increment
- g_rhs_roots (list of PyFelt of Fp2). A list of square roots over the given field.
At each attempt, if rhs(x) = x^3 + ax + b is not a quad residue (ie: the point is not on the curve),
x is updated by hashing it with the attempt : new_x = poseidon(x, attempt)
Since in a finite field, if z is not a quad residue, g*z is a quad residue (alternatively), at each attempt,
the square root of g*x is stored in the g*rhs_roots array.
This is used to verify the existence of the square roots in Cairo.
See the derive_ec_point_from_X cairo function in ec_ops.cairo.
"""
field = get_base_field(curve_id.value)
if isinstance(x, int):
x = field(x)

def rhs_compute(x: PyFelt | Fp2) -> PyFelt | Fp2:
"""
Compute the right hand side of the Weirstrass equation.
rhs(x) = x^3 + ax + b
"""
if isinstance(x, Fp2):
return (
x**3
Expand Down Expand Up @@ -144,6 +176,11 @@ def verify_ecip(
A0: G1Point | G2Point = None,
use_rust: bool = True,
) -> bool:
"""
Verifies the zk-ecip hint.
If Q, sum_dlog are not provided from a previous computation of the zk_ecip_hint, it will compute them.
If the random point A0 is not provided for verifying the hint, a random one will be sampled.
"""
# Prover :
if Q is None or sum_dlog is None:
Q, sum_dlog = zk_ecip_hint(Bs, scalars, use_rust)
Expand Down Expand Up @@ -217,6 +254,10 @@ def verify_ecip(
def slope_intercept(
P: G1Point | G2Point, Q: G1Point | G2Point
) -> tuple[PyFelt, PyFelt] | tuple[Fp2, Fp2]:
"""
Returns the slope and intercept of the line passing through points P and Q.
y = mx + b
"""
field = get_base_field(P.curve_id.value, get_field_type_from_ec_point(P))
if P == Q:
px, py = field(P.x), field(P.y)
Expand Down Expand Up @@ -307,6 +348,7 @@ class FF:
Represents a polynomial over F_p[x] or F_p^2[x]
Example : F(x, y) = c0(x) + c1(x) * y + c2(x) * y^2 + ...
where c0, c1, c2, ... are polynomials over F_p[x] or F_p^2[x]
Used to represent a subset of the Function Field where coefficients are polynomials instead of rational functions.
"""

coeffs: list[Polynomial[T]]
Expand Down Expand Up @@ -433,7 +475,7 @@ class EmptyListOfPoints(Exception):

def construct_function(Ps: list[G1Point] | list[G2Point]) -> FF:
"""
Returns a function exactly interpolating the points Ps.
Returns a function field element (class FF) exactly interpolating the points Ps.
"""
if len(Ps) == 0:
raise EmptyListOfPoints(
Expand Down Expand Up @@ -515,7 +557,7 @@ def ecip_functions(

def dlog(d: FF) -> FunctionFelt:
"""
Compute the logarithmic derivative of a Function
Compute the logarithmic derivative of a FunctionFieldElement (class FF),
Returns a FunctionFelt such that F = a(x) + b(x) * y with a, b RationalFunctions
Ref https://gist.github.com/Liam-Eagen/666d0771f4968adccd6087465b8c5bd4 (cell #12)
Expand Down
16 changes: 12 additions & 4 deletions hydra/garaga/hints/extf_mul.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@
from garaga.hints.tower_backup import E6, get_tower_object


# Returns (Q(X), R(X)) such that Π(Pi)(X) = Q(X) * P_irr(X) + R(X), for a given curve and extension degree.
# R(X) is the result of the multiplication in the extension field.
# Q(X) is used for verification.
def nondeterministic_extension_field_mul_divmod(
Ps: list[list[PyFelt | ModuloCircuitElement]],
curve_id: int,
extension_degree: int,
) -> tuple[list[PyFelt], list[PyFelt]]:
"""
From a list of Polynomials Ps = [P1, ..., Pn]
Returns (Q(X), R(X)) such that Π(Pi)(X) = Q(X) * P_irr(X) + R(X), for a given curve and extension degree.
R(X) is the result of the multiplication in the extension field.
Q(X) is used for verification.
"""
field = get_base_field(curve_id)
ps = [[c.value for c in P] for P in Ps]
q, r = garaga_rs.nondeterministic_extension_field_mul_divmod(
Expand All @@ -34,20 +37,25 @@ def nondeterministic_square_torus(
) -> list[PyFelt]:
"""
Computes 1/2 * (A(x) + x/A(x))
Deprecated usage as Torus based arithmetic is not used anymore with the final exp witness.
"""
A = direct_to_tower(A, curve_id, 6) if biject_from_direct else A
A = E6(A, curve_id)
SQ: list[PyFelt] = A.square_torus().felt_coeffs
return tower_to_direct(SQ, curve_id, 6) if biject_from_direct else SQ


# Returns (A/B)(X) mod P_irr(X)
def nondeterministic_extension_field_div(
A: list[PyFelt | ModuloCircuitElement],
B: list[PyFelt | ModuloCircuitElement],
curve_id: int,
extension_degree: int = 6,
) -> tuple[list[PyFelt], list[PyFelt]]:
"""
From two Polynomials A and B
Returns (A/B)(X) mod P_irr(X)
Converts back and forth to tower representaion for faster inversion.
"""

A = direct_to_tower(A, curve_id, extension_degree)
B = direct_to_tower(B, curve_id, extension_degree)
Expand Down
1 change: 1 addition & 0 deletions hydra/garaga/hints/frobenius.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def get_V_torus_powers(curve_id: int, extension_degree: int, k: int) -> Polynomi
"""
Computes 1/V^((p^k - 1) // 2) where V is the polynomial V(X) = X.
This is used to compute the Frobenius automorphism in the Torus.
Usage is deprecated since torus arithmetic is not used anymore with the final exp witness.
Args:
curve_id (int): Identifier for the curve.
Expand Down
5 changes: 4 additions & 1 deletion hydra/garaga/hints/multi_miller_witness.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@

def get_final_exp_witness(curve_id: int, f: E12) -> tuple[E12, E12]:
"""
Returns the witness for the final exponentiation step.
From a miller loop output f of a given curve ID such that f^h == 1,
Returns the witnesses c, w for the final exponentiation step, such that
- f*w = c^lambda
- w lies in a subfield of Fq12.
"""
if curve_id != CurveID.BN254.value and curve_id != CurveID.BLS12_381.value:
raise ValueError(f"Curve ID {curve_id} not supported")
Expand Down
2 changes: 1 addition & 1 deletion hydra/garaga/hints/neg_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def construct_digit_vectors(es: list[int]) -> list[list[int]]:
dss_ = [neg_3_base_le(e) for e in es] # Base -3 digits
max_len = max(len(ds) for ds in dss_)
dss_ = [ds + [0] * (max_len - len(ds)) for ds in dss_]
# Transposing the matrix equivalent in Python
# Transposing the matrix
dss = [[dss_[row][col] for row in range(len(dss_))] for col in range(max_len)]
return dss

Expand Down
4 changes: 4 additions & 0 deletions hydra/garaga/hints/tower_backup.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
"""
Tower based arithmetic for BN254 and BLS12-381 on Fq2, Fq6, Fq12.
"""

import random
from dataclasses import dataclass

Expand Down
13 changes: 10 additions & 3 deletions hydra/garaga/modulo_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ def non_interactive_transform(self) -> "ValueSegment":
def get_dw_lookups(self) -> dict:
"""
Returns the DW arrays for the compiled circuit.
Only relevant in Cairo 0 mode.
"""
assert self.compilation_mode == 0, "Only supported in Cairo 0 mode"
dw_arrays = {
"constants_ptr": [],
"add_offsets_ptr": [],
Expand Down Expand Up @@ -383,6 +383,10 @@ def write_element(
write_source: WriteOps = WriteOps.INPUT,
instruction: ModuloCircuitInstruction | None = None,
) -> ModuloCircuitElement:
"""
Register an emulated field element to the circuit given its value and the write source.
Returns a ModuloCircuitElement representing the written element with its offset as identifier.
"""
assert isinstance(elmt, PyFelt), f"Expected PyFelt, got {type(elmt)}"
value_offset = self.values_segment.write_to_segment(
ValueSegmentItem(
Expand Down Expand Up @@ -446,6 +450,9 @@ def write_elements(
return vals

def write_cairo_native_felt(self, native_felt: PyFelt):
assert (
self.compilation_mode == 0
), "write_cairo_native_felt is not supported in cairo 1 mode"
assert isinstance(
native_felt, PyFelt
), f"Expected PyFelt, got {type(native_felt)}"
Expand All @@ -461,10 +468,10 @@ def write_sparse_constant_elements(
for elmt, s in zip(elmts, sparsity):
match s:
case 0:
# Mocked 0 element. Be careful to pass sparsity when evaluating.
# Mocked 0 element not written to the circuit. Be careful to pass sparsity when evaluating.
elements.append(ModuloCircuitElement(self.field.zero(), -1))
case 2:
# Mocked 1 element. Be careful to pass sparsity when evaluating.
# Mocked 1 element not written to the circuit. Be careful to pass sparsity when evaluating.
elements.append(ModuloCircuitElement(self.field.one(), -1))
case _:
elements.append(self.set_or_get_constant(elmt.value))
Expand Down
3 changes: 2 additions & 1 deletion hydra/garaga/precompiled_circuits/all_circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
RHSFinalizeAccCircuit,
SlopeInterceptSamePointCircuit,
)
from garaga.starknet.cli.utils import create_directory


class CircuitID(Enum):
Expand Down Expand Up @@ -225,7 +226,7 @@ def main(
compilation_mode: int = 1,
):
"""Compiles and writes all circuits to .cairo files"""

create_directory(PRECOMPILED_CIRCUITS_DIR)
# Ensure the 'codes' dict keys match the filenames used for file creation.
# Using sets to remove potential duplicates
filenames_used = set([v["filename"] for v in CIRCUITS_TO_COMPILE.values()])
Expand Down
Loading

0 comments on commit 3781978

Please sign in to comment.