Skip to content

Commit

Permalink
Bytecode-Steps Tradeoff (#144)
Browse files Browse the repository at this point in the history
  • Loading branch information
feltroidprime authored Aug 9, 2024
1 parent 2194b9b commit b07f998
Show file tree
Hide file tree
Showing 68 changed files with 40,030 additions and 39,770 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cairo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ jobs:
scarb-version: "2.7.0"
- run: scarb fmt --check
working-directory: src/cairo
- run: cd src/cairo && scarb test
- run: cd src/cairo && scarb test
2 changes: 1 addition & 1 deletion .github/workflows/fustat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
uses: fkirc/skip-duplicate-actions@v5
with:
concurrent_skipping: 'same_content_newer'

test-fustat:
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ tools/garaga_rs/target/
tools/garaga_rs/Cargo.lock

src/cairo/target/
*target*
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ repos:
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
exclude: \.cairo$
- id: check-yaml
- id: check-toml
- repo: https://github.com/psf/black
Expand Down
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ build:
setup:
./tools/make/setup.sh

bytecode-check:
./tools/make/bytecode_check.sh

rewrite:
./tools/make/rewrite.sh

steps:
./tools/make/steps.sh


profile:
Expand All @@ -21,7 +29,7 @@ profile-no-compile:
./tools/make/launch_cairo_files.py -profile -no_compile
run:
@echo "A script to select, compile & run one Cairo file"
@echo "Total number of steps will be shown at the end of the run."
@echo "Total number of steps will be shown at the end of the run."
@echo "Thank you for testing Garaga!"
./tools/make/launch_cairo_files.py

Expand All @@ -32,7 +40,7 @@ run-no-compile:

run-pie:
@echo "A script to select, compile & run one Cairo file with pie mode enabled"
@echo "Total number of steps will be shown at the end of the run."
@echo "Total number of steps will be shown at the end of the run."
@echo "Thank you for proving Garaga!"
./tools/make/launch_cairo_files.py -pie
clean:
Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
> State-of-the-art Elliptic Curve tooling and SNARKS verification for Cairo & Starknet 🐺.
Garaga can enable efficient elliptic curve pairing and scalar multiplication operations on Starknet.
It achieves state of the art performance by
It achieves state of the art performance by
- using a dedicated builtin made by Starkware for emulated modular arithmetic
- using a suite of non-deterministic techniques for extension field multiplication, pairings, and multi scalar multiplication to reduce the number of steps to verifiy results.

Expand All @@ -38,21 +38,21 @@ Here are some interesting use cases enabled by Garaga:
- KZG cryptographic commitment scheme.
- Identity-based encryption schemes.
- Attribute-based encryption schemes.
- BLS (Boneh–Lynn–Shacham) Digital Signature scheme.
- BLS (Boneh–Lynn–Shacham) Digital Signature scheme.


## Architecture overview (in progress.)



Garaga consists of a Pythonic backend and CairoZero / Starknet interfaces.
- The Pythonic backend is here to define emulated modular arithmetic circuits that can be compiled to Cairo or Cairo1 code.
Garaga consists of a Pythonic backend and CairoZero / Starknet interfaces.
- The Pythonic backend is here to define emulated modular arithmetic circuits that can be compiled to Cairo or Cairo1 code.
It also handles witnesses generation for the non-deterministic computations.
- The CairoZero / Starknet interfaces are responsible for composing and calling the circuits, as well as adding all the extra logic needed to make the algorithms work (Fiat-Shamir heuristic, SNARKS verifiers, etc).
- The CairoZero / Starknet interfaces are responsible for composing and calling the circuits, as well as adding all the extra logic needed to make the algorithms work (Fiat-Shamir heuristic, SNARKS verifiers, etc).

## Deploying SNARKS verifier on Starknet

`pip install garaga` with tutorials coming.
`pip install garaga` with tutorials coming.
In the meantime, check `tools/starknet/`

## Development setup
Expand All @@ -62,8 +62,8 @@ To get started with Garaga, you'll need to have some tools and dependencies inst
### Prerequisites

Ensure you have the following installed:
- [Python 3.10](https://www.python.org/downloads/) - The core language used for development. Make sure you have the correct dependencies installed (namely, 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.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).
- [Python 3.10](https://www.python.org/downloads/) - The core language used for development. Make sure you have the correct dependencies installed (namely, 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.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 Expand Up @@ -164,7 +164,7 @@ See [LICENSE](LICENSE) for more information.
- Credits to [Nethermind](https://github.com/NethermindEth/) for their [initial work on optimized modular arithmetic](https://github.com/NethermindEth/research-basic-Cairo-operations-big-integers/tree/main/lib).
- [Herodotus](https://www.herodotus.dev/) for supporting this project.
- [Gnark project](https://github.com/ConsenSys/gnark-crypto) and team, especially [yelhousni](https://github.com/yelhousni) for his amazing knowledge and support.
- [OnlyDust](https://www.onlydust.xyz/) and [Starkware](https://starkware.co/).
- [OnlyDust](https://www.onlydust.xyz/) and [Starkware](https://starkware.co/).
- Liam Eagen and Andrija Novakovic for their support and amazing research.
## Resources
- Craig Costello, [Pairing for beginners](https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf)
Expand Down
9 changes: 4 additions & 5 deletions docs/hints_document.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ i+=1
- **[Lines 267-273](https://github.com/keep-starknet-strange/garaga/blob/main/src/fustat//utils.cairo#L267-L273)**

```python
from hydra.hints.io import bigint_split
from hydra.hints.io import bigint_split
felt_val = memory[ids.values_start+i-1]
limbs = bigint_split(felt_val, ids.N_LIMBS, ids.BASE)
assert limbs[3] == 0
Expand All @@ -152,7 +152,7 @@ ids.d0, ids.d1, ids.d2 = limbs[0], limbs[1], limbs[2]
- **[Lines 300-305](https://github.com/keep-starknet-strange/garaga/blob/main/src/fustat//utils.cairo#L300-L305)**

```python
from hydra.hints.io import bigint_split
from hydra.hints.io import bigint_split
limbs = bigint_split(ids.x, ids.N_LIMBS, ids.BASE)
assert limbs[3] == 0
ids.d0, ids.d1, ids.d2 = limbs[0], limbs[1], limbs[2]
Expand Down Expand Up @@ -462,7 +462,7 @@ print(f"\tN={ids.circuit.N_Euclidean_equations} felt252 from Poseidon transcript
- **[Lines 160-163](https://github.com/keep-starknet-strange/garaga/blob/main/src/fustat//modulo_circuit.cairo#L160-L163)**

```python
# Sanity Check :
# Sanity Check :
assert ids.Z == EXTF_MOD_CIRCUIT.transcript.continuable_hash, f"Z for circuit {EXTF_MOD_CIRCUIT.name} does not match {hex(ids.Z)} {hex(EXTF_MOD_CIRCUIT.transcript.continuable_hash)}"

```
Expand Down Expand Up @@ -509,7 +509,7 @@ print(f"\tZ = Hash(Init_Hash|Commitments) = Poseidon(Init_Hash, Poseidon({(ids.c
- **[Lines 254-257](https://github.com/keep-starknet-strange/garaga/blob/main/src/fustat//modulo_circuit.cairo#L254-L257)**

```python
# Sanity Check :
# Sanity Check :
assert ids.Z == EXTF_MOD_CIRCUIT.transcript.continuable_hash, f"Z for circuit {EXTF_MOD_CIRCUIT.name} does not match {hex(ids.Z)} {hex(EXTF_MOD_CIRCUIT.transcript.continuable_hash)}"

```
Expand All @@ -535,4 +535,3 @@ print(f"\tRunning ModuloBuiltin circuit...")
## File: [src/fustat/precompiled_circuits/final_exp.cairo](https://github.com/keep-starknet-strange/garaga/blob/main/src/fustat//precompiled_circuits/final_exp.cairo)

## File: [src/fustat/precompiled_circuits/multi_miller_loop.cairo](https://github.com/keep-starknet-strange/garaga/blob/main/src/fustat//precompiled_circuits/multi_miller_loop.cairo)

32 changes: 29 additions & 3 deletions hydra/extension_field_modulo_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,35 @@ def circuit_input(self):
for offset in sorted(self.values_segment.segment_stacks[WriteOps.INPUT])
]

def create_lines_z_powers(self, z: PyFelt):
powers = [z]
if self.curve_id == 0:
powers.append(self.square(z, "compute z^2")) # z^2 at index 1
powers.append(self.mul(powers[-1], z, "compute z^3")) # z^3 at index 2
powers.append(None) # No z^4
powers.append(None) # No z^5
powers.append(self.square(powers[2], "compute z^6")) # z^6 at index 5
powers.append(self.mul(powers[5], z, "compute z^7")) # z^7 at index 6
powers.append(None) # No z^8
powers.append(
self.mul(powers[6], powers[1], "compute z^9")
) # z^9 at index 8
self.z_powers = powers
elif self.curve_id == 1:
# Need z^2, z^3, z^6, Z^8:
powers.append(self.square(z, "compute z^2")) # z^2 at index 1
powers.append(self.mul(powers[-1], z, "compute z^3")) # z^3 at index 2
powers.append(None) # No z^4
powers.append(None) # No z^5
powers.append(self.square(powers[2], "compute z^6")) # z^6 at index 5
powers.append(None) # No z^7
powers.append(
self.mul(powers[5], powers[1], "compute z^8")
) # z^8 at index 4
self.z_powers = powers
else:
raise ValueError(f"Invalid curve id: {self.curve_id}")

def create_powers_of_Z(
self,
Z: PyFelt | ModuloCircuitElement,
Expand Down Expand Up @@ -190,9 +219,6 @@ def eval_poly_in_precomputed_Z(
"""
if poly_name is None:
poly_name = "UnnamedPoly"
assert len(X) - 1 <= len(
self.z_powers
), f"Degree {len(X)-1} > Zpowlen = {len(self.z_powers)}"

if sparsity:
first_non_zero_idx = next(
Expand Down
18 changes: 9 additions & 9 deletions hydra/hints/bls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
from hydra.hints.tower_backup import E12

"""
The goal of this script is to provide aux witness for efficient proving that Miller loop outputs of BLS12-381 curve are λ residues.
Co-factor h = 27 * p * h3 and λ = r * 3 * p^2.
We know that when pairings are equal multi Miller loop output is always r-th residue,
but it is λ residue only when it is a cube and p-th residue too.
This happens with a very small probability (1/3*p), but both 27th and p-th roots of unity are in the subfields of Fp12, Fp3 and Fp respectively.
Thus we can simply clear their contribution and not affect he soundness of the method.
The goal of this script is to provide aux witness for efficient proving that Miller loop outputs of BLS12-381 curve are λ residues.
Co-factor h = 27 * p * h3 and λ = r * 3 * p^2.
We know that when pairings are equal multi Miller loop output is always r-th residue,
but it is λ residue only when it is a cube and p-th residue too.
This happens with a very small probability (1/3*p), but both 27th and p-th roots of unity are in the subfields of Fp12, Fp3 and Fp respectively.
Thus we can simply clear their contribution and not affect he soundness of the method.
Note that gcd(27*p, h3) = 1, therefore we do the following:
Note that gcd(27*p, h3) = 1, therefore we do the following:
i := inverse_mod(h3, 27*p)
s := -i % 27*p
Then given the Miller loop output x, computing w = (x^h3)^s gives exactly the inverse of 27*p-th root of unity contribution in x.
By computing x_sh = w * x we are left with an element that is of order exactly h3.
Then given the Miller loop output x, computing w = (x^h3)^s gives exactly the inverse of 27*p-th root of unity contribution in x.
By computing x_sh = w * x we are left with an element that is of order exactly h3.
Since gcd(h1, λ) = 1 computing λ root of x_sh can be simply done by just raising x_sh^e where e = inverse_mod(h3, λ)
"""
Expand Down
2 changes: 1 addition & 1 deletion hydra/hints/ecip.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ def print_ff(ff: FF):
import random

from hydra.definitions import STARK
from hydra.hints.io import int_to_u384, int_array_to_u384_array
from hydra.hints.io import int_array_to_u384_array, int_to_u384

random.seed(0)

Expand Down
7 changes: 5 additions & 2 deletions hydra/hints/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@ def int_array_to_u256_array(x: list[int] | list[PyFelt]) -> str:
return f"array![{', '.join([int_to_u256(i) for i in x])}]"


def int_array_to_u384_array(x: list[int] | list[PyFelt]) -> str:
return f"array![{', '.join([int_to_u384(i) for i in x])}]"
def int_array_to_u384_array(x: list[int] | list[PyFelt], const=False) -> str:
if const:
return f"[{', '.join([int_to_u384(i) for i in x])}]"
else:
return f"array![{', '.join([int_to_u384(i) for i in x])}]"


def bigint_pack(x: object, n_limbs: int, base: int) -> int:
Expand Down
50 changes: 36 additions & 14 deletions hydra/modulo_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -894,15 +894,32 @@ def write_cairo1_input_stack(
return code, offset_to_reference_map, start_index

def fill_cairo_1_constants(self) -> str:
constants = [
bigint_split(self.values_segment.segment[offset].value, 4, 2**96)
constants_ints = [
self.values_segment.segment[offset].value
for offset in self.values_segment.segment_stacks[WriteOps.CONSTANT].keys()
]
constants_filled = "\n".join(
f"circuit_inputs = circuit_inputs.next([{','.join(hex(x) for x in constants[i])}]); // in{i}"
for i in range(len(constants))
)
return constants_filled
if len(constants_ints) < 8:
constants_split = [bigint_split(x, N_LIMBS, BASE) for x in constants_ints]
constants_filled = "\n".join(
f"circuit_inputs = circuit_inputs.next_2([{','.join(hex(x) for x in constants_split[i])}]); // in{i}"
for i in range(len(constants_ints))
)
return constants_filled, None
else:
import hydra.hints.io as io

const_name = (
self.name.upper()
+ "_"
+ CurveID(self.curve_id).name.upper()
+ "_CONSTANTS"
)
constants_filled = f"""
circuit_inputs = circuit_inputs.next_span({const_name}.span()); // in{0} - in{len(constants_ints)-1}
"""

const_array = f"const {const_name}: [u384; {len(constants_ints)}] = {io.int_array_to_u384_array(constants_ints, const=True)};"
return constants_filled, const_array

def write_cairo1_circuit(self, offset_to_reference_map: dict[int, str]) -> str:
code = ""
Expand Down Expand Up @@ -1030,12 +1047,16 @@ def compile_circuit_cairo_1(
"""

code += f"""
let mut circuit_inputs = ({','.join(outputs_refs_needed)},).new_inputs();
// Prefill constants:
{self.fill_cairo_1_constants()}
// Fill inputs:
"""

tmp, const_array = self.fill_cairo_1_constants()
code += tmp
code += """
// Fill inputs:
"""

acc_len = len(self.values_segment.segment_stacks[WriteOps.CONSTANT])
if input_is_struct:
for struct in self.input_structs:
Expand All @@ -1062,10 +1083,7 @@ def compile_circuit_cairo_1(
}};
"""
code += f"""
let outputs = match circuit_inputs.done().eval(modulus) {{
Result::Ok(outputs) => {{ outputs }},
Result::Err(_) => {{ panic!("Expected success") }}
}};
let outputs = circuit_inputs.done_2().eval(modulus).unwrap();
"""
if return_is_struct:
code += "\n".join(
Expand All @@ -1082,6 +1100,10 @@ def compile_circuit_cairo_1(
code += f"let res=array![{','.join([f'outputs.get_output({ref})' for ref in outputs_refs])}];\n"
code += "return res;\n"
code += "}\n"

if const_array:
code += "\n"
code += const_array
return code, function_name

def summarize(self):
Expand Down
Loading

0 comments on commit b07f998

Please sign in to comment.