From 99d8f1a4a77abe31886c878b041aa03e0ddedbcd Mon Sep 17 00:00:00 2001 From: David Rusu Date: Fri, 17 May 2024 14:00:03 +0400 Subject: [PATCH] feat(cl/noir): provide an ergonomic Noir api for use within Python. --- coordination-layer/README.md | 25 +++++++ coordination-layer/__init__.py | 0 coordination-layer/noir/.gitignore | 4 ++ coordination-layer/noir/Nargo.toml | 2 + coordination-layer/noir/README.md | 18 +++++ .../noir/crates/bigger/Nargo.toml | 7 ++ .../noir/crates/bigger/src/main.nr | 19 +++++ coordination-layer/noir_constraint.py | 71 +++++++++++++++++++ coordination-layer/test_noir_constraint.py | 20 ++++++ requirements.txt | 5 +- 10 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 coordination-layer/README.md create mode 100644 coordination-layer/__init__.py create mode 100644 coordination-layer/noir/.gitignore create mode 100644 coordination-layer/noir/Nargo.toml create mode 100644 coordination-layer/noir/README.md create mode 100644 coordination-layer/noir/crates/bigger/Nargo.toml create mode 100644 coordination-layer/noir/crates/bigger/src/main.nr create mode 100644 coordination-layer/noir_constraint.py create mode 100644 coordination-layer/test_noir_constraint.py diff --git a/coordination-layer/README.md b/coordination-layer/README.md new file mode 100644 index 0000000..f0ddbfa --- /dev/null +++ b/coordination-layer/README.md @@ -0,0 +1,25 @@ +# Coordination Layer + +This module provides the executable specifications of the Coordination Layer (CL). + + +## Setup + +We are currently experimenting with the Noir Language. In order to run the specification, you will need to install Noir. + +Follow the instructions here: +https://noir-lang.org/docs/getting_started/installation/ + +Verify the installation by running + +```bash +noirup +``` + +## Tests + +From the repository root run: + +```bash +python -m unittest -v coordination-layer/test_* +``` diff --git a/coordination-layer/__init__.py b/coordination-layer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/coordination-layer/noir/.gitignore b/coordination-layer/noir/.gitignore new file mode 100644 index 0000000..3251ad6 --- /dev/null +++ b/coordination-layer/noir/.gitignore @@ -0,0 +1,4 @@ +crates/*/Prover.toml +crates/*/Verifier.toml +proofs/*.proof +target/* \ No newline at end of file diff --git a/coordination-layer/noir/Nargo.toml b/coordination-layer/noir/Nargo.toml new file mode 100644 index 0000000..a0865ab --- /dev/null +++ b/coordination-layer/noir/Nargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["crates/bigger"] diff --git a/coordination-layer/noir/README.md b/coordination-layer/noir/README.md new file mode 100644 index 0000000..90f93ee --- /dev/null +++ b/coordination-layer/noir/README.md @@ -0,0 +1,18 @@ +# Noir Circuits + +this directory holds all the circuits written in Noir, used by the CL specification. + +Each circuit is it's own nargo package under the `crates/` directory. + +## Creating a new circuit + +1. inside `crates/`, run `nargo new ` +2. update `./Nargo.toml` to include the new circuit in the workspace + +## Testing circuits + +Under `./noir`, simple run. + +``` +nargo test +``` diff --git a/coordination-layer/noir/crates/bigger/Nargo.toml b/coordination-layer/noir/crates/bigger/Nargo.toml new file mode 100644 index 0000000..9480e15 --- /dev/null +++ b/coordination-layer/noir/crates/bigger/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bigger" +type = "bin" +authors = [""] +compiler_version = ">=0.22.0" + +[dependencies] \ No newline at end of file diff --git a/coordination-layer/noir/crates/bigger/src/main.nr b/coordination-layer/noir/crates/bigger/src/main.nr new file mode 100644 index 0000000..476e71f --- /dev/null +++ b/coordination-layer/noir/crates/bigger/src/main.nr @@ -0,0 +1,19 @@ +fn main(x: u32, y: pub u32) { + assert(x > y); +} + +#[test] +fn test_bigger() { + main(3, 2); +} + + +#[test(should_fail)] +fn test_equal() { + main(2, 2); +} + +#[test(should_fail)] +fn test_smaller() { + main(1, 2); +} diff --git a/coordination-layer/noir_constraint.py b/coordination-layer/noir_constraint.py new file mode 100644 index 0000000..efd82f4 --- /dev/null +++ b/coordination-layer/noir_constraint.py @@ -0,0 +1,71 @@ +""" +This module provides the interface for loading, proving and verifying constraints written in noir. + +The assumptions of this module: +- noir constraints are defined as a noir package in `./noir/crates//` +- ./noir is relative to this file +- noir constraints have already been compiled. + +For ergonomics, one should provide python wrappers that understands the API of +the corresponding constraint. +""" + +from dataclasses import dataclass +from pathlib import Path + +import sh +import portalocker +import tempfile +import toml + +NOIR_DIR = Path(__file__).resolve().parent / "noir" +LOCK_FILE = NOIR_DIR / ".CL.lock" +CONSTRAINTS_DIR = NOIR_DIR / "crates" + +NARGO = sh.Command("nargo") + + +@dataclass +class Proof: + proof: str + + +class NoirConstraint: + def __init__(self, name: str): + self.name = name + assert self.noir_package_dir.exists() and self.noir_package_dir.is_dir() + self._prepare() + + @property + def noir_package_dir(self): + return CONSTRAINTS_DIR / self.name + + def prove(self, params: dict): + with portalocker.TemporaryFileLock(LOCK_FILE): + with open(self.noir_package_dir / "Prover.toml", "w") as prover_f: + toml.dump(params, prover_f) + + prove_res = self._nargo("prove", _return_cmd=True) + assert prove_res.exit_code == 0 + + with open(NOIR_DIR / "proofs" / f"{self.name}.proof", "r") as proof: + return Proof(proof.read()) + + def verify(self, params: dict, proof: Proof): + with portalocker.TemporaryFileLock(LOCK_FILE): + with open(self.noir_package_dir / "Verifier.toml", "w") as verifier_f: + toml.dump(params, verifier_f) + + with open(NOIR_DIR / "proofs" / f"{self.name}.proof", "w") as proof_file: + proof_file.write(proof.proof) + verify_res = self._nargo("verify", _ok_code=[0, 1], _return_cmd=True) + return verify_res.exit_code == 0 + + def _nargo(self, *args, **kwargs): + return NARGO(*args, **kwargs, _cwd=self.noir_package_dir) + + def _prepare(self): + check = self._nargo("check", _return_cmd=True) + assert check.exit_code == 0 + compile = self._nargo("compile", _return_cmd=True) + assert compile.exit_code == 0 diff --git a/coordination-layer/test_noir_constraint.py b/coordination-layer/test_noir_constraint.py new file mode 100644 index 0000000..4b0c026 --- /dev/null +++ b/coordination-layer/test_noir_constraint.py @@ -0,0 +1,20 @@ +from unittest import TestCase + +from .noir_constraint import NoirConstraint + + +class TestNoirCoinstraint(TestCase): + def test_bigger(self): + # simple constraint that proves we know a number bigger than the provided + # public input. + bigger = NoirConstraint("bigger") + + # x is the secret input, y is the public input + proof = bigger.prove({"x": "5", "y": "3"}) + + # The proof that we know an `x` that is bigger than `y` should verify + # Note, we must provide the public input that was used in the proof. + assert bigger.verify({"y": "3"}, proof) + + # If we change the public input, the proof fails to verify. + assert not bigger.verify({"y": "4"}, proof) diff --git a/requirements.txt b/requirements.txt index bb86124..b0bbe16 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,7 @@ pycparser==2.21 pysphinx==0.0.1 scipy==1.11.4 black==23.12.1 -sympy==1.12 \ No newline at end of file +sympy==1.12 +sh==2.0.6 +toml==0.10.2 +portalocker==2.8.2