Skip to content

Commit

Permalink
Aztec brother: Keccak Honk contract support. (#245)
Browse files Browse the repository at this point in the history
  • Loading branch information
feltroidprime authored Dec 9, 2024
1 parent 73095cf commit 245c09f
Show file tree
Hide file tree
Showing 71 changed files with 23,136 additions and 78,778 deletions.
4 changes: 4 additions & 0 deletions hydra/garaga/algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ def __rtruediv__(self, left: PyFelt | int) -> PyFelt:
return self.__inv__().__mul__(left)

def is_quad_residue(self) -> bool:
if self.value == 0:
return True
return legendre_symbol(self.value, self.p) == 1

def sqrt(self, min_root: bool = True) -> PyFelt:
Expand Down Expand Up @@ -390,6 +392,8 @@ class ModuloCircuitElement:
emulated_felt: PyFelt
offset: int

__repr__ = lambda self: f"ModuloCircuitElement({hex(self.value)}, {self.offset})"

@property
def value(self) -> int:
return self.emulated_felt.value
Expand Down
24 changes: 24 additions & 0 deletions hydra/garaga/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,19 @@
SECP256K1_ID = 2
SECP256R1_ID = 3
ED25519_ID = 4
GRUMPKIN_ID = 5


class ProofSystem(Enum):
Groth16 = "groth16"
UltraKeccakHonk = "ultra_keccak_honk"

@property
def supported_curves(self) -> set[int]:
if self == ProofSystem.Groth16:
return {BN254_ID, BLS12_381_ID}
if self == ProofSystem.UltraKeccakHonk:
return {BN254_ID}
return set()


Expand All @@ -43,6 +47,7 @@ class CurveID(Enum):
SECP256K1 = 2
SECP256R1 = 3
ED25519 = 4
GRUMPKIN = 5

@staticmethod
def from_str(s: str) -> "CurveID":
Expand Down Expand Up @@ -428,6 +433,19 @@ def bit(value, index):
Gy=0x6666666666666666666666666666666666666666666666666666666666666658,
swu_params=None,
),
GRUMPKIN_ID: WeierstrassCurve(
cairo_zero_namespace_name="grumpkin",
id=GRUMPKIN_ID,
p=0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001,
n=0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD47,
h=1,
a=0,
b=-17 % 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001,
fp_generator=5,
Gx=0x1,
Gy=0x2CF135E7506A45D632D270D45F1181294833FC48D823F272C,
swu_params=None,
),
}


Expand Down Expand Up @@ -550,6 +568,9 @@ class G1Point:
curve_id: CurveID
iso_point: bool = False

def __repr__(self) -> str:
return f"G1Point({hex(self.x)}, {hex(self.y)}) on {self.curve_id.value}"

def __str__(self) -> str:
return f"G1Point({self.x}, {self.y}) on curve {self.curve_id}"

Expand Down Expand Up @@ -853,6 +874,9 @@ class G2Point:
y: tuple[int, int]
curve_id: CurveID

def __repr__(self):
return f"G2Point({hex(self.x[0])}, {hex(self.x[1])}, {hex(self.y[0])}, {hex(self.y[1])}, {self.curve_id})"

def __post_init__(self):
assert isinstance(CURVES[self.curve_id.value], PairingCurve)
if self.is_infinity():
Expand Down
9 changes: 9 additions & 0 deletions hydra/garaga/hints/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,19 @@ def int_to_u256(x: int | PyFelt) -> str:
return f"u256{{low:{hex(limbs[0])}, high:{hex(limbs[1])}}}"


def int_to_u128(x: int | PyFelt) -> str:
assert 0 <= x < 2**128, f"Value {x} is too large to fit in a u128"
return hex(x)


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_u128_array(x: list[int] | list[PyFelt]) -> str:
return f"array![{', '.join([int_to_u128(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])}]"
Expand Down
56 changes: 42 additions & 14 deletions hydra/garaga/modulo_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from garaga.definitions import BASE, CURVES, N_LIMBS, STARK, CurveID, get_sparsity
from garaga.hints.extf_mul import nondeterministic_extension_field_div
from garaga.hints.io import bigint_split
from garaga.modulo_circuit_structs import Cairo1SerializableStruct
from garaga.modulo_circuit_structs import Cairo1SerializableStruct, u384

BATCH_SIZE = 1 # Batch Size, only used in cairo 0 mode.

Expand Down Expand Up @@ -380,15 +380,19 @@ def is_empty_circuit(self) -> bool:

def write_element(
self,
elmt: PyFelt,
elmt: PyFelt | int,
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)}"
assert isinstance(elmt, PyFelt) or isinstance(
elmt, int
), f"Expected PyFelt or int, got {type(elmt)}"
if isinstance(elmt, int):
elmt = self.field(elmt)
value_offset = self.values_segment.write_to_segment(
ValueSegmentItem(
elmt,
Expand Down Expand Up @@ -418,7 +422,7 @@ def write_struct(

if all_pyfelt:
self.input_structs.append(struct)
if len(struct) == 1:
if len(struct) == 1 and isinstance(struct, u384):
return self.write_element(struct.elmts[0], write_source)
else:
return self.write_elements(struct.elmts, write_source)
Expand All @@ -432,7 +436,10 @@ def write_struct(
return result

def write_elements(
self, elmts: list[PyFelt], operation: WriteOps, sparsity: list[int] = None
self,
elmts: list[PyFelt],
operation: WriteOps = WriteOps.INPUT,
sparsity: list[int] = None,
) -> list[ModuloCircuitElement]:
if sparsity is not None:
assert len(sparsity) == len(
Expand Down Expand Up @@ -509,6 +516,24 @@ def add(
a.emulated_felt + b.emulated_felt, WriteOps.BUILTIN, instruction
)

def sum(self, args: list[ModuloCircuitElement], comment: str | None = None):
if not args:
raise ValueError("The 'args' list cannot be empty.")
assert all(isinstance(elmt, ModuloCircuitElement) for elmt in args)
result = args[0]
for elmt in args[1:]:
result = self.add(result, elmt, comment)
return result

def product(self, args: list[ModuloCircuitElement], comment: str | None = None):
if not args:
raise ValueError("The 'args' list cannot be empty.")
assert all(isinstance(elmt, ModuloCircuitElement) for elmt in args)
result = args[0]
for elmt in args[1:]:
result = self.mul(result, elmt, comment)
return result

def double(self, a: ModuloCircuitElement) -> ModuloCircuitElement:
return self.add(a, a)

Expand All @@ -524,7 +549,7 @@ def mul(
return self.set_or_get_constant(0)
assert isinstance(a, ModuloCircuitElement) and isinstance(
b, ModuloCircuitElement
), f"Expected ModuloElement, got {type(a)}, {a} and {type(b)}, {b}"
), f"Expected ModuloElement, got lhs {type(a)}, {a} and rhs {type(b)}, {b}"
instruction = ModuloCircuitInstruction(
ModBuiltinOps.MUL, a.offset, b.offset, self.values_offset, comment
)
Expand Down Expand Up @@ -827,14 +852,14 @@ def extend_struct_output(self, struct: Cairo1SerializableStruct):
def print_value_segment(self):
self.values_segment.print()

def compile_circuit(self, function_name: str = None):
def compile_circuit(self, function_name: str = None, pub: bool = True):
if self.is_empty_circuit():
return "", ""
self.values_segment = self.values_segment.non_interactive_transform()
if self.compilation_mode == 0:
return self.compile_circuit_cairo_zero(function_name), None
elif self.compilation_mode == 1:
return self.compile_circuit_cairo_1(function_name)
return self.compile_circuit_cairo_1(function_name, pub)

def compile_circuit_cairo_zero(
self,
Expand Down Expand Up @@ -1062,6 +1087,7 @@ def write_cairo1_circuit(self, offset_to_reference_map: dict[int, str]) -> str:
def compile_circuit_cairo_1(
self,
function_name: str = None,
pub: bool = False,
) -> str:
"""
Defines the Cairo 1 function code for the compiled circuit.
Expand Down Expand Up @@ -1097,10 +1123,14 @@ def compile_circuit_cairo_1(
else:
signature_input = "mut input: Array<u384>"

if pub:
prefix = "pub "
else:
prefix = ""
if self.generic_circuit:
code = f"#[inline(always)]\npub fn {function_name}({signature_input}, curve_index:usize)->{signature_output} {{\n"
code = f"#[inline(always)]\n{prefix}fn {function_name}({signature_input}, curve_index:usize)->{signature_output} {{\n"
else:
code = f"#[inline(always)]\npub fn {function_name}({signature_input})->{signature_output} {{\n"
code = f"#[inline(always)]\n{prefix}fn {function_name}({signature_input})->{signature_output} {{\n"

# Define the input for the circuit.
code, offset_to_reference_map, start_index = self.write_cairo1_input_stack(
Expand Down Expand Up @@ -1145,9 +1175,7 @@ def compile_circuit_cairo_1(
"""
else:
code += """
let modulus = get_p(curve_index);
let modulus = TryInto::<_, CircuitModulus>::try_into([modulus.limb0, modulus.limb1, modulus.limb2, modulus.limb3])
.unwrap();
let modulus = get_modulus(curve_index);
"""

code += f"""
Expand Down Expand Up @@ -1175,7 +1203,7 @@ def compile_circuit_cairo_1(
)
else:
struct_code_with_counter = (
struct_code + f" // in{acc_len} - in{acc_len+len(struct)-1}"
struct_code + f" // in{acc_len} - in{acc_len+len(struct)-1}\n"
)
acc_len += len(struct)
code += struct_code_with_counter + "\n"
Expand Down
Loading

0 comments on commit 245c09f

Please sign in to comment.