feat(cl/noir): provide an ergonomic Noir api for use within Python.

This commit is contained in:
David Rusu 2024-05-17 14:00:03 +04:00
parent 9390d481ba
commit 99d8f1a4a7
10 changed files with 170 additions and 1 deletions

View File

@ -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_*
```

View File

4
coordination-layer/noir/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
crates/*/Prover.toml
crates/*/Verifier.toml
proofs/*.proof
target/*

View File

@ -0,0 +1,2 @@
[workspace]
members = ["crates/bigger"]

View File

@ -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 <circuit name>`
2. update `./Nargo.toml` to include the new circuit in the workspace
## Testing circuits
Under `./noir`, simple run.
```
nargo test
```

View File

@ -0,0 +1,7 @@
[package]
name = "bigger"
type = "bin"
authors = [""]
compiler_version = ">=0.22.0"
[dependencies]

View File

@ -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);
}

View File

@ -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/<constraint>/`
- ./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

View File

@ -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)

View File

@ -6,4 +6,7 @@ pycparser==2.21
pysphinx==0.0.1
scipy==1.11.4
black==23.12.1
sympy==1.12
sympy==1.12
sh==2.0.6
toml==0.10.2
portalocker==2.8.2