From 3781978612fdc2f9cb389070751f1abdae8e99a0 Mon Sep 17 00:00:00 2001 From: feltroid Prime <96737978+feltroidprime@users.noreply.github.com> Date: Fri, 30 Aug 2024 22:22:29 +0200 Subject: [PATCH] bump cairo to 2.8.0 & add comments in hydra (#178) --- .github/workflows/cairo.yml | 2 +- .github/workflows/e2e.yml | 2 +- .gitignore | 2 +- Makefile | 9 +- README.md | 2 +- docs/gitbook/installation/developer-setup.md | 2 +- hydra/garaga/hints/ecip.py | 50 +++- hydra/garaga/hints/extf_mul.py | 16 +- hydra/garaga/hints/frobenius.py | 1 + hydra/garaga/hints/multi_miller_witness.py | 5 +- hydra/garaga/hints/neg_3.py | 2 +- hydra/garaga/hints/tower_backup.py | 4 + hydra/garaga/modulo_circuit.py | 13 +- .../precompiled_circuits/all_circuits.py | 3 +- .../compilable_circuits/base.py | 41 +++- .../cairo1_mpcheck_circuits.py | 84 +++++-- .../compilable_circuits/fustat_only.py | 228 ------------------ .../garaga/precompiled_circuits/final_exp.py | 5 + .../groth16_contract_generator/generator.py | 6 +- pyproject.toml | 4 +- src/Scarb.lock | 6 - src/Scarb.toml | 2 +- .../groth16_example_bls12_381/Scarb.lock | 13 - .../groth16_example_bls12_381/Scarb.toml | 2 +- .../src/groth16_verifier.cairo | 2 +- .../groth16_example_bn254/Scarb.lock | 13 - .../groth16_example_bn254/Scarb.toml | 2 +- .../src/groth16_verifier.cairo | 2 +- src/contracts/universal_ecip/Scarb.lock | 13 - src/contracts/universal_ecip/Scarb.toml | 2 +- tools/make/ci_cairo.sh | 12 + tools/make/ci_e2e.sh | 11 +- tools/make/ci_hydra.sh | 12 + tools/make/gen_hints_document.py | 90 ------- tools/make/rewrite.sh | 5 + tools/make/setup.sh | 6 +- 36 files changed, 243 insertions(+), 431 deletions(-) delete mode 100644 hydra/garaga/precompiled_circuits/compilable_circuits/fustat_only.py delete mode 100644 src/Scarb.lock delete mode 100644 src/contracts/groth16_example_bls12_381/Scarb.lock delete mode 100644 src/contracts/groth16_example_bn254/Scarb.lock delete mode 100644 src/contracts/universal_ecip/Scarb.lock create mode 100755 tools/make/ci_cairo.sh create mode 100755 tools/make/ci_hydra.sh delete mode 100755 tools/make/gen_hints_document.py diff --git a/.github/workflows/cairo.yml b/.github/workflows/cairo.yml index a2b8086d..cd574c9e 100644 --- a/.github/workflows/cairo.yml +++ b/.github/workflows/cairo.yml @@ -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 diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 41be50f7..d4918386 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -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 diff --git a/.gitignore b/.gitignore index 68e1f5a0..084b3394 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Makefile b/Makefile index 2fb37283..3259aa70 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index b25c9d92..31847110 100644 --- a/README.md +++ b/README.md @@ -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 : diff --git a/docs/gitbook/installation/developer-setup.md b/docs/gitbook/installation/developer-setup.md index cfb5696b..bfc59363 100644 --- a/docs/gitbook/installation/developer-setup.md +++ b/docs/gitbook/installation/developer-setup.md @@ -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) : diff --git a/hydra/garaga/hints/ecip.py b/hydra/garaga/hints/ecip.py index e6b29205..bc1926d6 100644 --- a/hydra/garaga/hints/ecip.py +++ b/hydra/garaga/hints/ecip.py @@ -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): @@ -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): @@ -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 @@ -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) @@ -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) @@ -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]] @@ -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( @@ -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) diff --git a/hydra/garaga/hints/extf_mul.py b/hydra/garaga/hints/extf_mul.py index f58e4c5c..e7e9a86a 100644 --- a/hydra/garaga/hints/extf_mul.py +++ b/hydra/garaga/hints/extf_mul.py @@ -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( @@ -34,6 +37,7 @@ 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) @@ -41,13 +45,17 @@ def nondeterministic_square_torus( 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) diff --git a/hydra/garaga/hints/frobenius.py b/hydra/garaga/hints/frobenius.py index 4b419648..5a53e06d 100644 --- a/hydra/garaga/hints/frobenius.py +++ b/hydra/garaga/hints/frobenius.py @@ -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. diff --git a/hydra/garaga/hints/multi_miller_witness.py b/hydra/garaga/hints/multi_miller_witness.py index 6c182d1e..3032d06d 100644 --- a/hydra/garaga/hints/multi_miller_witness.py +++ b/hydra/garaga/hints/multi_miller_witness.py @@ -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") diff --git a/hydra/garaga/hints/neg_3.py b/hydra/garaga/hints/neg_3.py index 923e0746..6284ab33 100644 --- a/hydra/garaga/hints/neg_3.py +++ b/hydra/garaga/hints/neg_3.py @@ -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 diff --git a/hydra/garaga/hints/tower_backup.py b/hydra/garaga/hints/tower_backup.py index 1dcd8f5d..4893e397 100644 --- a/hydra/garaga/hints/tower_backup.py +++ b/hydra/garaga/hints/tower_backup.py @@ -1,3 +1,7 @@ +""" +Tower based arithmetic for BN254 and BLS12-381 on Fq2, Fq6, Fq12. +""" + import random from dataclasses import dataclass diff --git a/hydra/garaga/modulo_circuit.py b/hydra/garaga/modulo_circuit.py index 6c09d6f9..c7bbd4d8 100644 --- a/hydra/garaga/modulo_circuit.py +++ b/hydra/garaga/modulo_circuit.py @@ -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": [], @@ -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( @@ -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)}" @@ -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)) diff --git a/hydra/garaga/precompiled_circuits/all_circuits.py b/hydra/garaga/precompiled_circuits/all_circuits.py index 1401a133..7a7043ce 100644 --- a/hydra/garaga/precompiled_circuits/all_circuits.py +++ b/hydra/garaga/precompiled_circuits/all_circuits.py @@ -34,6 +34,7 @@ RHSFinalizeAccCircuit, SlopeInterceptSamePointCircuit, ) +from garaga.starknet.cli.utils import create_directory class CircuitID(Enum): @@ -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()]) diff --git a/hydra/garaga/precompiled_circuits/compilable_circuits/base.py b/hydra/garaga/precompiled_circuits/compilable_circuits/base.py index 9ab706c9..293fa094 100644 --- a/hydra/garaga/precompiled_circuits/compilable_circuits/base.py +++ b/hydra/garaga/precompiled_circuits/compilable_circuits/base.py @@ -11,12 +11,21 @@ class BaseModuloCircuit(ABC): """ - Base class for all modulo circuits. + Base class for all modulo circuits that will be compiled to Cairo code. Parameters: - name: str, the name of the circuit - - input_len: int, the number of input elements (/!\ of total felt252 values) + - input_len: int, the number of input elements. + The actual number set here is not always used, + - curve_id: int, the id of the curve - auto_run: bool, whether to run the circuit automatically at initialization. + When compiling, this flag is set to true so the ModuloCircuit class inside the + ".circuit" member of this class holds the necessary metadata + about the operations that will be compiled. + For CairoZero, this flag will be set to False in the Python hint, so that + BaseModuloCircuit.run_circuit() can be called on a segment parsed from the + CairoZero VM. + - compilation mode: 0 (CairoZero) or 1 (Cairo) """ def __init__( @@ -28,7 +37,6 @@ def __init__( compilation_mode: int = 0, ) -> None: self.name = name - self.cairo_name = int.from_bytes(name.encode("utf-8"), "big") self.curve_id = curve_id self.field = get_base_field(curve_id) self.input_len = input_len @@ -41,7 +49,11 @@ def __init__( @abstractmethod def build_input(self) -> list[PyFelt]: - pass + """ + This method is used to create the necessary inputs that will be written to the ModuloCircuit. + It works in pair with the _run_circuit_inner function, where the _run_circuit_inner will use the output of + the build_input function to "deserialize" the list of elements and write them to the ModuloCircuit class. + """ @property def full_input_cairo1(self) -> list[PyFelt] | list[Cairo1SerializableStruct]: @@ -55,9 +67,23 @@ def full_input_cairo1(self) -> list[PyFelt] | list[Cairo1SerializableStruct]: @abstractmethod def _run_circuit_inner(self, input: list[PyFelt]) -> ModuloCircuit: - pass + """ + This method is responsible for + - deserializing the input list of elements, + - creating a ModuloCircuit class (or class that derives from ModuloCircuit) + - "writing" the inputs to the ModuloCircuit class to obtain ModuloCircuitElements + - using the methods add, sub, mul, inv (or higher level methods) of the ModuloCircuit class + to define the list of operations on the given inputs + - Returning the ModuloCircuit class in a state where the circuit has been run, and therefore holding + the metadata so that its instructions can be compiled to Cairo code. + + """ def run_circuit(self, input: list[int]) -> ModuloCircuit: + """ + A simple wrapper around _run_circuit_inner that converts the input as a list of ints to a list of PyFelt. + Used in CairoZero hint. + """ # print( # f"Running circuit for {self.name} with CurveID {CurveID(self.curve_id).name}..." # ) @@ -66,6 +92,11 @@ def run_circuit(self, input: list[int]) -> ModuloCircuit: class BaseEXTFCircuit(BaseModuloCircuit): + """ + A extension of the BaseModuloCircuit class that holds an init_hash, used for CairoZero. + Not relevant for Cairo1 circuits. + """ + def __init__( self, name: str, diff --git a/hydra/garaga/precompiled_circuits/compilable_circuits/cairo1_mpcheck_circuits.py b/hydra/garaga/precompiled_circuits/compilable_circuits/cairo1_mpcheck_circuits.py index aea0c216..69c0ab27 100644 --- a/hydra/garaga/precompiled_circuits/compilable_circuits/cairo1_mpcheck_circuits.py +++ b/hydra/garaga/precompiled_circuits/compilable_circuits/cairo1_mpcheck_circuits.py @@ -1,9 +1,13 @@ from abc import ABC, abstractmethod -from typing import Callable, Dict, List, Tuple, Type, Union +from typing import Dict, List, Optional, Tuple, Type, Union import garaga.modulo_circuit_structs as structs from garaga.definitions import BLS12_381_ID, BN254_ID, get_irreducible_poly -from garaga.extension_field_modulo_circuit import ExtensionFieldModuloCircuit, PyFelt +from garaga.extension_field_modulo_circuit import ( + ExtensionFieldModuloCircuit, + ModuloCircuit, + PyFelt, +) from garaga.modulo_circuit_structs import ( E12D, BLSProcessedPair, @@ -20,9 +24,9 @@ from garaga.precompiled_circuits.compilable_circuits.base import BaseEXTFCircuit -def split_list_into_pairs( +def split_4_sized_object_into_tuple_of_2_size( input: Union[List[PyFelt], Tuple[PyFelt, PyFelt, PyFelt, PyFelt]] -) -> Tuple[Tuple[PyFelt, PyFelt], Tuple[PyFelt, PyFelt]]: +) -> Optional[Tuple[List[PyFelt], List[PyFelt]]]: if input is None: return None assert len(input) == 4, f"Expected input of length 4, got {len(input)}" @@ -41,11 +45,11 @@ def parse_precomputed_g1_consts_and_g2_points( circuit.yInv.append(vars[f"yInv_{i}"]) circuit.xNegOverY.append(vars[f"xNegOverY_{i}"]) current_points.append( - split_list_into_pairs(vars.get(f"Q_{i}", None)) + split_4_sized_object_into_tuple_of_2_size(vars.get(f"Q_{i}", None)) ) # Return empty list if not present if bit_1: q_or_q_neg_points.append( - split_list_into_pairs( + split_4_sized_object_into_tuple_of_2_size( vars.get(f"Q_or_Q_neg_{i}", None) ) # Return empty list if not present ) @@ -98,13 +102,27 @@ def _initialize_circuit(self): @property @abstractmethod - def input_map(self) -> Dict[str, Union[Type[Cairo1SerializableStruct], Callable]]: + def input_map( + self, + ) -> Dict[ + str, + Union[ + Type[Cairo1SerializableStruct], Tuple[Type[Cairo1SerializableStruct], int] + ], + ]: """ - Define the input map for the circuit. - For u384Array, use a tuple (u384Array, size) to specify its size. + Define the input map for the circuit in a dict. + The key will be the name of the input variable, also used in the signature of the compiled Cairo code. + The value will be either a Cairo1SerializableStruct type (which defines the struct in the Cairo code), + or a tuple of the type and its size (for Array-like types). + The reason behind this is that each Cairo1SerializableStruct defines the __len__ method, but for the + array-like structs we need to specify the size in advance. """ - def _base_input_map(self, bit_type): + def _base_input_map(self, bit_type: str) -> dict: + """ + Base input map for the bit 0, 1, and 00 cases. + """ input_map = {} # Add pair inputs @@ -139,8 +157,16 @@ def _base_input_map(self, bit_type): return input_map def _process_input( - self, circuit: multi_pairing_check.MultiPairingCheckCircuit, input - ): + self, circuit: multi_pairing_check.MultiPairingCheckCircuit, input: list[PyFelt] + ) -> dict: + """ + Method responsible for deserializing the input list of elements into the variables in the input map, + and writing them to the circuit. + The input list is expected to be in the same order as the input map. + Since we use Python 3.10, the input map dict is ordered. + Returns a vars dict with the same keys as the input map, but with the values being the instances of the structs, + each struct holding ModuloCircuitElement(s). + """ vars = {} for name, struct_info in self.input_map.items(): if isinstance(struct_info, tuple) and struct_info[0] == u384Array: @@ -182,10 +208,13 @@ def _process_input( return vars def build_input(self) -> list[PyFelt]: + """ + Extends the base method of BaseModuloCircuit, by reading the input map and returning a list of random elements of the total expected size. + """ total_elements = 0 for name, struct_info in self.input_map.items(): if isinstance(struct_info, tuple): - # This is the u384Array case + # Array-like case _, size = struct_info total_elements += size else: @@ -195,14 +224,14 @@ def build_input(self) -> list[PyFelt]: return [self.field.random() for _ in range(total_elements)] - def _run_circuit_inner(self, input: list[PyFelt]): + def _run_circuit_inner(self, input: list[PyFelt]) -> ModuloCircuit: circuit = self._initialize_circuit() vars = self._process_input(circuit, input) return self._execute_circuit_logic(circuit, vars) @abstractmethod - def _execute_circuit_logic(self, circuit, vars): + def _execute_circuit_logic(self, circuit, vars) -> ModuloCircuit: """ Implement the circuit logic using the processed input variables. """ @@ -231,9 +260,6 @@ def _execute_circuit_bit_logic_base(self, circuit, vars, bit_type): if bit_type == "1": sum_i_prod_k_P = circuit.mul(sum_i_prod_k_P, vars["c_or_cinv_of_z"]) - # f_i_plus_one_of_z = circuit.eval_poly_in_precomputed_Z( - # vars["f_i_plus_one"], poly_name="f_i+1" - # ) f_i_plus_one_of_z = vars["f_i_plus_one_of_z"] new_lhs = circuit.mul( ci_plus_one, @@ -355,7 +381,7 @@ def __init__( def input_map(self): return self._base_input_map("0") - def _execute_circuit_logic(self, circuit, vars): + def _execute_circuit_logic(self, circuit, vars) -> ModuloCircuit: return self._execute_circuit_bit_logic_base(circuit, vars, "0") @@ -381,7 +407,7 @@ def __init__( def input_map(self): return self._base_input_map("00") - def _execute_circuit_logic(self, circuit, vars): + def _execute_circuit_logic(self, circuit, vars) -> ModuloCircuit: return self._execute_circuit_bit_logic_base(circuit, vars, "00") @@ -408,7 +434,7 @@ def __init__( def input_map(self): return self._base_input_map("1") - def _execute_circuit_logic(self, circuit, vars): + def _execute_circuit_logic(self, circuit, vars) -> ModuloCircuit: return self._execute_circuit_bit_logic_base(circuit, vars, "1") @@ -457,7 +483,7 @@ def input_map(self): input_map["previous_lhs"] = u384 return input_map - def _execute_circuit_logic(self, circuit, vars): + def _execute_circuit_logic(self, circuit, vars) -> ModuloCircuit: n_pairs = self.n_pairs current_points, _ = parse_precomputed_g1_consts_and_g2_points( circuit, vars, n_pairs @@ -607,10 +633,16 @@ def _execute_circuit_logic( n_pairs = self.n_pairs current_points = [] for k in range(n_pairs): - circuit.Q.append(split_list_into_pairs(vars.get(f"original_Q{k}", None))) + circuit.Q.append( + split_4_sized_object_into_tuple_of_2_size( + vars.get(f"original_Q{k}", None) + ) + ) circuit.yInv.append(vars[f"yInv_{k}"]) circuit.xNegOverY.append(vars[f"xNegOverY_{k}"]) - current_points.append(split_list_into_pairs(vars.get(f"Q_{k}", None))) + current_points.append( + split_4_sized_object_into_tuple_of_2_size(vars.get(f"Q_{k}", None)) + ) circuit.create_powers_of_Z(vars["z"], max_degree=12) @@ -707,7 +739,7 @@ def input_map(self): "Q": (u384Array, self.max_q_degree + 1), } - def _execute_circuit_logic(self, circuit, vars): + def _execute_circuit_logic(self, circuit, vars) -> ModuloCircuit: if self.curve_id == BN254_ID: return circuit @@ -795,7 +827,7 @@ def _initialize_circuit(self): compilation_mode=self.compilation_mode, ) - def _execute_circuit_logic(self, circuit, vars): + def _execute_circuit_logic(self, circuit, vars) -> ModuloCircuit: n_pairs = self.n_pairs for i in range(n_pairs): p = vars[f"p_{i}"] diff --git a/hydra/garaga/precompiled_circuits/compilable_circuits/fustat_only.py b/hydra/garaga/precompiled_circuits/compilable_circuits/fustat_only.py deleted file mode 100644 index cd1a6c95..00000000 --- a/hydra/garaga/precompiled_circuits/compilable_circuits/fustat_only.py +++ /dev/null @@ -1,228 +0,0 @@ -from random import randint - -from garaga.definitions import CURVES, STARK, CurveID -from garaga.extension_field_modulo_circuit import ExtensionFieldModuloCircuit -from garaga.modulo_circuit import WriteOps -from garaga.precompiled_circuits import ( - final_exp, - multi_miller_loop, - multi_pairing_check, -) -from garaga.precompiled_circuits.compilable_circuits.base import ( - BaseEXTFCircuit, - BaseModuloCircuit, - ModuloCircuit, - PyFelt, -) -from garaga.precompiled_circuits.ec import DerivePointFromX - - -class DerivePointFromXCircuit(BaseModuloCircuit): - def __init__( - self, curve_id: int, auto_run: bool = True, compilation_mode: int = 0 - ) -> None: - super().__init__( - name="derive_point_from_x", - input_len=3, # X + b + G - curve_id=curve_id, - auto_run=auto_run, - compilation_mode=compilation_mode, - ) - - def build_input(self) -> list[PyFelt]: - input = [] - input.append(self.field(randint(0, STARK - 1))) - input.append(self.field(CURVES[self.curve_id].a)) - input.append(self.field(CURVES[self.curve_id].b)) # y^2 = x^3 + b - input.append(self.field(CURVES[self.curve_id].fp_generator)) - return input - - def _run_circuit_inner(self, input: list[PyFelt]) -> ModuloCircuit: - circuit = DerivePointFromX( - self.name, self.curve_id, compilation_mode=self.compilation_mode - ) - x, a, b, g = circuit.write_elements(input[0:4], WriteOps.INPUT) - rhs, grhs, should_be_rhs, should_be_grhs, y_try = circuit._derive_point_from_x( - x, a, b, g - ) - circuit.extend_output([rhs, grhs, should_be_rhs, should_be_grhs, y_try]) - - return circuit - - -class FP12MulCircuit(BaseEXTFCircuit): - def __init__( - self, - curve_id: int, - auto_run: bool = True, - init_hash: int = None, - compilation_mode: int = 0, - ): - super().__init__( - "fp12_mul", 24, curve_id, auto_run, init_hash, compilation_mode - ) - - def build_input(self) -> list[PyFelt]: - return [self.field(randint(0, self.field.p - 1)) for _ in range(self.input_len)] - - def _run_circuit_inner(self, input: list[PyFelt]) -> ExtensionFieldModuloCircuit: - circuit = ExtensionFieldModuloCircuit( - self.name, - self.curve_id, - extension_degree=12, - init_hash=self.init_hash, - compilation_mode=self.compilation_mode, - ) - X = circuit.write_elements(input[0:12], WriteOps.INPUT) - Y = circuit.write_elements(input[12:24], WriteOps.INPUT) - xy = circuit.extf_mul([X, Y], 12) - circuit.extend_output(xy) - circuit.finalize_circuit() - - return circuit - - -class FinalExpPart1Circuit(BaseEXTFCircuit): - def __init__( - self, - curve_id: int, - auto_run: bool = True, - init_hash: int = None, - compilation_mode: int = 0, - ): - super().__init__( - "final_exp_part_1", 12, curve_id, auto_run, init_hash, compilation_mode - ) - - def build_input(self) -> list[PyFelt]: - return [self.field(randint(0, self.field.p - 1)) for _ in range(self.input_len)] - - def _run_circuit_inner(self, input: list[PyFelt]) -> ExtensionFieldModuloCircuit: - circuit: final_exp.FinalExpTorusCircuit = final_exp.GaragaFinalExp[ - CurveID(self.curve_id) - ](name="final_exp_part_1", init_hash=self.init_hash) - t0, t1, _sum = circuit.final_exp_part1(input[0:6], input[6:12]) - # for t0_val in t0: - # print(f"Final exp Part1 t0 {hex(t0_val.value)}") - # for t1_val in t1: - # print(f"Final exp Part1 t1 {hex(t1_val.value)}") - # for _sum_val in _sum: - # print(f"Final exp Part1 _sum {hex(_sum_val.value)}") - # Note : output is handled inside final_exp_part1. - circuit.finalize_circuit() - - return circuit - - -class FinalExpPart2Circuit(BaseEXTFCircuit): - def __init__( - self, - curve_id: int, - auto_run: bool = True, - init_hash: int = None, - compilation_mode: int = 0, - ): - super().__init__( - "final_exp_part_2", 12, curve_id, auto_run, init_hash, compilation_mode - ) - - def build_input(self) -> list[PyFelt]: - return [self.field(randint(0, self.field.p - 1)) for _ in range(self.input_len)] - - def _run_circuit_inner(self, input: list[PyFelt]) -> ExtensionFieldModuloCircuit: - circuit: final_exp.FinalExpTorusCircuit = final_exp.GaragaFinalExp[ - CurveID(self.curve_id) - ](name="final_exp_part_2", hash_input=False, init_hash=self.init_hash) - res = circuit.final_exp_finalize(input[0:6], input[6:12]) - circuit.extend_output(res) - - return circuit - - -class MultiMillerLoop(BaseEXTFCircuit): - def __init__( - self, - curve_id: int, - n_pairs: int = 0, - auto_run: bool = True, - compilation_mode: int = 0, - ): - self.n_pairs = n_pairs - super().__init__( - "multi_miller_loop", 6 * n_pairs, curve_id, auto_run, compilation_mode - ) - self.generic_over_curve = True - - def build_input(self) -> list[PyFelt]: - curve_id = CurveID(self.curve_id) - order = CURVES[self.curve_id].n - input = [] - for _ in range(self.n_pairs): - n1, n2 = randint(1, order), randint(1, order) - p1, p2 = G1Point.get_nG(curve_id, n1), G2Point.get_nG(curve_id, n2) - pair = [p1.x, p1.y, p2.x[0], p2.x[1], p2.y[0], p2.y[1]] - input.extend([self.field(x) for x in pair]) - return input - - def _run_circuit_inner(self, input: list[PyFelt]): - assert ( - len(input) % 6 == 0 - ), f"Input length must be a multiple of 6, got {len(input)}" - n_pairs = len(input) // 6 - circuit = multi_miller_loop.MultiMillerLoopCircuit( - self.name, - self.curve_id, - n_pairs=n_pairs, - hash_input=True, - ) - circuit.write_p_and_q_raw(input) - - m = circuit.miller_loop(n_pairs) - - circuit.extend_output(m) - circuit.finalize_circuit() - - return circuit - - -class MultiPairingCheck(BaseEXTFCircuit): - def __init__( - self, - curve_id: int, - n_pairs: int = 0, - auto_run: bool = True, - compilation_mode: int = 0, - ): - self.n_pairs = n_pairs - super().__init__( - "multi_miller_loop", 6 * n_pairs, curve_id, auto_run, compilation_mode - ) - self.generic_over_curve = True - - def build_input(self) -> list[PyFelt]: - - input, _ = multi_pairing_check.get_pairing_check_input( - CurveID(self.curve_id), self.n_pairs - ) - return input - - def _run_circuit_inner(self, input: list[PyFelt]): - assert ( - len(input) % 6 == 0 - ), f"Input length must be a multiple of 6, got {len(input)}" - n_pairs = len(input) // 6 - assert n_pairs >= 2, f"n_pairs must be >= 2, got {n_pairs}" - circuit = multi_pairing_check.MultiPairingCheckCircuit( - self.name, - self.curve_id, - n_pairs=n_pairs, - hash_input=True, - ) - circuit.write_p_and_q_raw(input) - - m, _, _, _, _ = circuit.multi_pairing_check(n_pairs) - - circuit.extend_output(m) - circuit.finalize_circuit() - - return circuit diff --git a/hydra/garaga/precompiled_circuits/final_exp.py b/hydra/garaga/precompiled_circuits/final_exp.py index b0b11db6..267e750a 100644 --- a/hydra/garaga/precompiled_circuits/final_exp.py +++ b/hydra/garaga/precompiled_circuits/final_exp.py @@ -1,3 +1,8 @@ +""" +Deprecated Final Exp Circuits since we now use the final exp witness. +Kept for reference/in case we need it in the future. +""" + import copy from random import randint diff --git a/hydra/garaga/starknet/groth16_contract_generator/generator.py b/hydra/garaga/starknet/groth16_contract_generator/generator.py index 8782f3f2..7b506f01 100644 --- a/hydra/garaga/starknet/groth16_contract_generator/generator.py +++ b/hydra/garaga/starknet/groth16_contract_generator/generator.py @@ -7,7 +7,7 @@ from garaga.starknet.cli.utils import create_directory from garaga.starknet.groth16_contract_generator.parsing_utils import Groth16VerifyingKey -ECIP_OPS_CLASS_HASH = 0x29AEFD3C293B3D97A9CAF77FAC5F3C23A6AB8C7E70190CE8D7A12AC71CEAC4C +ECIP_OPS_CLASS_HASH = 0x25BDBB933FDBEF07894633039AACC53FDC1F89C6CF8A32324B5FEFDCC3D329E def precompute_lines_from_vk(vk: Groth16VerifyingKey) -> StructArray: @@ -29,7 +29,7 @@ def gen_groth16_verifier( vk: str | Path | Groth16VerifyingKey, output_folder_path: str, output_folder_name: str, - ecip_class_hash: ECIP_OPS_CLASS_HASH, + ecip_class_hash: int = ECIP_OPS_CLASS_HASH, cli_mode: bool = False, ) -> str: if isinstance(vk, (Path, str)): @@ -181,7 +181,7 @@ def gen_groth16_verifier( [dependencies] garaga = {{ {'git = "https://github.com/keep-starknet-strange/garaga.git"' if cli_mode else 'path = "../../"'} }} -starknet = "2.7.1" +starknet = "2.8.0" [cairo] sierra-replace-ids = false diff --git a/pyproject.toml b/pyproject.toml index 743d6332..088da4f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,14 +4,14 @@ build-backend = "maturin" [project] name = "garaga" -version = "0.13.2.2" +version = "0.13.2.3" requires-python = ">=3.10,<3.11" dependencies = [ "fastecdsa", "sympy", "typer", "python-dotenv", - "starknet-py-unbroken==0.24.3" + "starknet-py-unbroken==0.24.5" ] [project.optional-dependencies] diff --git a/src/Scarb.lock b/src/Scarb.lock deleted file mode 100644 index 21c19920..00000000 --- a/src/Scarb.lock +++ /dev/null @@ -1,6 +0,0 @@ -# Code generated by scarb DO NOT EDIT. -version = 1 - -[[package]] -name = "garaga" -version = "0.1.0" diff --git a/src/Scarb.toml b/src/Scarb.toml index 151bc4cd..b08f993d 100644 --- a/src/Scarb.toml +++ b/src/Scarb.toml @@ -13,6 +13,6 @@ repository = "https://github.com/keep-starknet-strange/garaga" [dependencies] [dev-dependencies] -cairo_test = "2.7.1" +cairo_test = "2.8.0" [cairo] sierra-replace-ids = false diff --git a/src/contracts/groth16_example_bls12_381/Scarb.lock b/src/contracts/groth16_example_bls12_381/Scarb.lock deleted file mode 100644 index 404dbb53..00000000 --- a/src/contracts/groth16_example_bls12_381/Scarb.lock +++ /dev/null @@ -1,13 +0,0 @@ -# Code generated by scarb DO NOT EDIT. -version = 1 - -[[package]] -name = "garaga" -version = "0.1.0" - -[[package]] -name = "groth16_example_bls12_381" -version = "0.1.0" -dependencies = [ - "garaga", -] diff --git a/src/contracts/groth16_example_bls12_381/Scarb.toml b/src/contracts/groth16_example_bls12_381/Scarb.toml index 33012d79..a04ad964 100644 --- a/src/contracts/groth16_example_bls12_381/Scarb.toml +++ b/src/contracts/groth16_example_bls12_381/Scarb.toml @@ -5,7 +5,7 @@ edition = "2024_07" [dependencies] garaga = { path = "../../" } -starknet = "2.7.1" +starknet = "2.8.0" [cairo] sierra-replace-ids = false diff --git a/src/contracts/groth16_example_bls12_381/src/groth16_verifier.cairo b/src/contracts/groth16_example_bls12_381/src/groth16_verifier.cairo index c4603688..4636bcaa 100644 --- a/src/contracts/groth16_example_bls12_381/src/groth16_verifier.cairo +++ b/src/contracts/groth16_example_bls12_381/src/groth16_verifier.cairo @@ -25,7 +25,7 @@ mod Groth16VerifierBLS12_381 { use super::{N_PUBLIC_INPUTS, vk, ic, precomputed_lines}; const ECIP_OPS_CLASS_HASH: felt252 = - 0x29aefd3c293b3d97a9caf77fac5f3c23a6ab8c7e70190ce8d7a12ac71ceac4c; + 0x25bdbb933fdbef07894633039aacc53fdc1f89c6cf8a32324b5fefdcc3d329e; use starknet::ContractAddress; #[storage] diff --git a/src/contracts/groth16_example_bn254/Scarb.lock b/src/contracts/groth16_example_bn254/Scarb.lock deleted file mode 100644 index 3fe2c4dc..00000000 --- a/src/contracts/groth16_example_bn254/Scarb.lock +++ /dev/null @@ -1,13 +0,0 @@ -# Code generated by scarb DO NOT EDIT. -version = 1 - -[[package]] -name = "garaga" -version = "0.1.0" - -[[package]] -name = "groth16_example_bn254" -version = "0.1.0" -dependencies = [ - "garaga", -] diff --git a/src/contracts/groth16_example_bn254/Scarb.toml b/src/contracts/groth16_example_bn254/Scarb.toml index 73584052..1fe1e73e 100644 --- a/src/contracts/groth16_example_bn254/Scarb.toml +++ b/src/contracts/groth16_example_bn254/Scarb.toml @@ -5,7 +5,7 @@ edition = "2024_07" [dependencies] garaga = { path = "../../" } -starknet = "2.7.1" +starknet = "2.8.0" [cairo] sierra-replace-ids = false diff --git a/src/contracts/groth16_example_bn254/src/groth16_verifier.cairo b/src/contracts/groth16_example_bn254/src/groth16_verifier.cairo index a78cb9c6..ba6da6bf 100644 --- a/src/contracts/groth16_example_bn254/src/groth16_verifier.cairo +++ b/src/contracts/groth16_example_bn254/src/groth16_verifier.cairo @@ -25,7 +25,7 @@ mod Groth16VerifierBN254 { use super::{N_PUBLIC_INPUTS, vk, ic, precomputed_lines}; const ECIP_OPS_CLASS_HASH: felt252 = - 0x29aefd3c293b3d97a9caf77fac5f3c23a6ab8c7e70190ce8d7a12ac71ceac4c; + 0x25bdbb933fdbef07894633039aacc53fdc1f89c6cf8a32324b5fefdcc3d329e; use starknet::ContractAddress; #[storage] diff --git a/src/contracts/universal_ecip/Scarb.lock b/src/contracts/universal_ecip/Scarb.lock deleted file mode 100644 index b295fae9..00000000 --- a/src/contracts/universal_ecip/Scarb.lock +++ /dev/null @@ -1,13 +0,0 @@ -# Code generated by scarb DO NOT EDIT. -version = 1 - -[[package]] -name = "garaga" -version = "0.1.0" - -[[package]] -name = "universal_ecip" -version = "0.1.0" -dependencies = [ - "garaga", -] diff --git a/src/contracts/universal_ecip/Scarb.toml b/src/contracts/universal_ecip/Scarb.toml index 3d11c2ff..2f4a7c45 100644 --- a/src/contracts/universal_ecip/Scarb.toml +++ b/src/contracts/universal_ecip/Scarb.toml @@ -5,7 +5,7 @@ edition = "2024_07" [dependencies] garaga = { path = "../.." } -starknet = "2.7.1" +starknet = "2.8.0" [cairo] sierra-replace-ids = false diff --git a/tools/make/ci_cairo.sh b/tools/make/ci_cairo.sh new file mode 100755 index 00000000..08ea4354 --- /dev/null +++ b/tools/make/ci_cairo.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +if command -v act &> /dev/null +then + act -W .github/workflows/cairo.yml --secret-file .secrets +elif command -v gh &> /dev/null && gh act --version &> /dev/null +then + gh act -W .github/workflows/cairo.yml --secret-file .secrets +else + echo "Error: Neither 'act' nor 'gh act' command found. Please install one of them to proceed." + exit 1 +fi diff --git a/tools/make/ci_e2e.sh b/tools/make/ci_e2e.sh index 875f4d06..9133ad81 100755 --- a/tools/make/ci_e2e.sh +++ b/tools/make/ci_e2e.sh @@ -1,3 +1,12 @@ #!/bin/bash -act -W .github/workflows/e2e.yml --secret-file .secrets +if command -v act &> /dev/null +then + act -W .github/workflows/e2e.yml --secret-file .secrets +elif command -v gh &> /dev/null && gh act --version &> /dev/null +then + gh act -W .github/workflows/e2e.yml --secret-file .secrets +else + echo "Error: Neither 'act' nor 'gh act' command found. Please install one of them to proceed." + exit 1 +fi diff --git a/tools/make/ci_hydra.sh b/tools/make/ci_hydra.sh new file mode 100755 index 00000000..0e590ad5 --- /dev/null +++ b/tools/make/ci_hydra.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +if command -v act &> /dev/null +then + act -W .github/workflows/hydra.yml --secret-file .secrets +elif command -v gh &> /dev/null && gh act --version &> /dev/null +then + gh act -W .github/workflows/hydra.yml --secret-file .secrets +else + echo "Error: Neither 'act' nor 'gh act' command found. Please install one of them to proceed." + exit 1 +fi diff --git a/tools/make/gen_hints_document.py b/tools/make/gen_hints_document.py deleted file mode 100755 index d2510ba2..00000000 --- a/tools/make/gen_hints_document.py +++ /dev/null @@ -1,90 +0,0 @@ -#!venv/bin/python3 -import os - - -def gen_hints_document( - root_folder: str, - repo_base_url: str = "https://github.com/keep-starknet-strange/garaga", - output_file: str = "docs/hints_document.md", -): - data = {} - for dirpath, dirnames, filenames in os.walk(root_folder): - for filename in filenames: - if filename.endswith(".cairo"): - print(f"dirpath: {dirpath}, dirnames: {dirnames}, filename: {filename}") - file_path = os.path.join(dirpath, filename) - hints = find_hints(file_path) - data[file_path] = hints - print(data.keys()) - with open(output_file, "w") as md_file: - for filepath, hints in data.items(): - relative_path = os.path.relpath(filepath, root_folder) - github_file_url = f"{repo_base_url}/blob/main/{root_folder}/{relative_path.replace(os.sep, '/')}" - md_file.write(f"## File: [{filepath}]({github_file_url})\n\n") - for func_name, hints_list in hints.items(): - md_file.write(f"### func: {func_name}\n\n") # Function name as a header - for hint in hints_list: - start_line, end_line = hint["lines"] - line_range = f"Lines {start_line}-{end_line}" - github_line_url = f"{github_file_url}#L{start_line}-L{end_line}" - md_file.write(f"- **[{line_range}]({github_line_url})**\n\n") - md_file.write( - f"```python\n{hint['content']}\n```\n\n" - ) # Hint content in a code block - return data - - -def find_hints(cairo_file_path: str): - def find_function_context(i: int, lines: list): - while i >= 0 and "func" not in lines[i]: - i -= 1 - if i >= 0: - func_line = lines[i] - name = func_line.split("func")[1] - name = name.split("(")[0].split("{")[0].strip() - return name - else: - return "Unknown" - - with open(cairo_file_path, "r") as f: - lines = f.readlines() - max_index = len(lines) - 1 - i = 0 - - res = {} - while i <= max_index: - if "%{" in lines[i]: - if "%}" in lines[i]: - # This is a one-line hint - hint_content = lines[i].split("%{")[1].split("%}")[0].strip() - func_name = find_function_context(i, lines) - if func_name not in res: - res[func_name] = [] - res[func_name].append({"lines": (i, i), "content": hint_content}) - i += 1 - - else: - hint_start = i - func_name = find_function_context(hint_start, lines) - hint_content = "" - i += 1 - leading_spaces = len(lines[i]) - len(lines[i].lstrip(" ")) - - while i <= max_index and "%}" not in lines[i]: - hint_content += lines[i][leading_spaces:] - i += 1 - hint_end = i - if func_name not in res: - res[func_name] = [] - res[func_name].append( - {"lines": (hint_start, hint_end), "content": hint_content} - ) - else: - i += 1 - - return res - - -if __name__ == "__main__": - folder = "src/fustat/" - gen_hints_document(folder) diff --git a/tools/make/rewrite.sh b/tools/make/rewrite.sh index 77b03a39..803f0abc 100755 --- a/tools/make/rewrite.sh +++ b/tools/make/rewrite.sh @@ -1,3 +1,8 @@ rm -rf src/src/tests/ +rm -rf src/src/circuits/ +rm -rf src/contracts/groth16_example_bls12_381 +rm -rf src/contracts/groth16_example_bn254 + python hydra/garaga/precompiled_circuits/all_circuits.py python hydra/garaga/starknet/tests_and_calldata_generators/test_writer.py +python hydra/garaga/starknet/groth16_contract_generator/generator.py diff --git a/tools/make/setup.sh b/tools/make/setup.sh index 34fabd42..3487c8fb 100755 --- a/tools/make/setup.sh +++ b/tools/make/setup.sh @@ -100,8 +100,8 @@ maturin develop --release echo "All done!" # Check Scarb version and print warning if it's not -if ! scarb --version | grep -q "2.7.1"; then - echo "Warning: Scarb is not installed or its version is not 2.7.1." +if ! scarb --version | grep -q "2.8.0"; then + echo "Warning: Scarb is not installed or its version is not 2.8.0." echo "Got: $(scarb --version)" - echo "Please install Scarb 2.7.1 before continuing. https://docs.swmansion.com/scarb/download.html" + echo "Please install Scarb 2.8.0 before continuing. https://docs.swmansion.com/scarb/download.html" fi