Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(docs): Documenting noir codegen #4454

Merged
merged 9 commits into from
Apr 2, 2024
Prev Previous commit
Next Next commit
Refine codegen doc
jzaki committed Mar 28, 2024
commit 3147d67a94e2ea0ac9d0b39cafdab68bb9b71166
120 changes: 48 additions & 72 deletions docs/docs/getting_started/tooling/noir_codegen.md
Original file line number Diff line number Diff line change
@@ -5,133 +5,109 @@ keywords: [Nargo, Noir, compile, TypeScript]
sidebar_position: 2
---

You can generate TypeScript bindings with `noir_codegen`, allowing for easier and faster integration of your Noir circuits into TypeScript projects.
When using typescript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained.

It is used in conjunction with `nargo export` which generates JSON for specified functions in a Noir library (not binary or contract circuits).
Now you can generate typescript bindings for your Noir programs in two steps:
1. Exporting Noir functions using `nargo export`
2. Using the typescript module `noir_codegen` to generate typescript binding

**Note:** you can only export functions from a Noir *library* (not binary or contract program types).

## Installation

You can install `noir_codegen` into your project using `yarn` or `npm`:
### Your TypeScript project

If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it:

```bash
yarn add typescript -D
npx tsc --init
```

### Add TypeScript module - `noir_codegen`

The following command will add the module to your project's devDependencies:

```bash
yarn add @noir-lang/noir_codegen
yarn add @noir-lang/noir_codegen -D
```

or
### Nargo library
Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../installation/index.md).

If you're in a new project, make a `circuits` folder and create a new Noir library:

```bash
npm install @noir-lang/noir_codegen
mkdir circuits && cd circuits
nargo new --lib myNoirLib
```

## Usage

### Export ABI of specified functions

Use the `#[export]` macro to indicate which functions in the noir library that you'd like to export.
First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript.

```rust
#[export]
fn your_function(...
```

The following command, when run from the directory with `Nargo.toml`, will create a folder with a .json file per exported function:
From your Noir library (where `Nargo.toml` is), run the following command:

```bash
jzaki marked this conversation as resolved.
Show resolved Hide resolved
nargo export
jzaki marked this conversation as resolved.
Show resolved Hide resolved
jzaki marked this conversation as resolved.
Show resolved Hide resolved
```
jzaki marked this conversation as resolved.
Show resolved Hide resolved
The .json files live in a new `exports` directory.

You can also specify the directory of Noir programs using `--program-dir` as follows:
You will now have an `export` directory with a .json file per exported function.

You can also specify the directory of Noir programs using `--program-dir`, for example:

```bash
nargo export --program-dir=./path/to/your/noir/program
nargo export --program-dir=./circuits/myNoirLib
```

### Run noir-codegen

You should now have a folder "exports" with your compiled function. To turn it into a TS binding, we will use the `noir-codegen` package we installed.
### Generate TypeScript bindings from exported functions

If you installed it globally, just pass the path of your exported JSON file(s) (you can use wildcards too, ex. `*.json`). :
To use the `noir-codegen` package we added to the TypeScript project:

```bash
yarn noir-codegen ./export/your_function.json
```

jzaki marked this conversation as resolved.
Show resolved Hide resolved
An `exports` directory is created with an index.ts file containing all exported functions.
This creates an `exports` directory with an `index.ts` file containing all exported functions.

You can optionally specify an output dir for your Typescript bindings to live with `--out-dir`:
**Note:** adding `--out-dir` allows you to specify an output dir for your Typescript bindings to go. Eg:

```bash
yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir
```

jzaki marked this conversation as resolved.
Show resolved Hide resolved
## Example .nr function to .ts output

## Example

For example, if you have a Noir program like this:
Consider a Noir library with this function:

```rust
struct MyStruct {
foo: bool,
bar: [str<5>; 3],
baz: Field
}

struct NestedStruct {
foo: MyStruct,
bar: [MyStruct; 3],
baz: u64
}
#[export]
fn exported_function_foo(x: u64, y: u64, array: [u8; 5], my_struct: NestedStruct, string: str<5>) -> (u64, u64, MyStruct) {
assert(array.len() == 5);
assert(my_struct.foo.foo);
assert(string == "12345");

print(x);
assert(x < y);
(x + y, 3, my_struct.foo)
}

fn unexported_function(x: u64, y: u64) {
assert(x != y);
fn not_equal(x: Field, y: Field) -> bool {
x != y
}
```

After following the usage instructions, you will have an `index.ts` that looks like this:
After the export and codegen steps, you should have an `index.ts` like:

```typescript
/* Autogenerated file, do not edit! */

/* eslint-disable */

import { Noir, InputMap, CompiledCircuit, ForeignCallHandler } from "@noir-lang/noir_js"

export { ForeignCallHandler } from "@noir-lang/noir_js"

export type u64 = string;
export type u8 = string | number;
export type Field = string;

export type MyStruct = {
foo: boolean;
bar: string[];
baz: Field;
};

export type NestedStruct = {
foo: MyStruct;
bar: MyStruct[];
baz: u64;
};


export const exported_function_foo_circuit: CompiledCircuit = {"abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"array","type":{"kind":"array","length":5,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"},{"name":"my_struct","type":{"kind":"struct","path":"NestedStruct","fields":[{"name":"foo","type":{"kind":"struct","path":"MyStruct","fields":[{"name":"foo","type":{"kind":"boolean"}},{"name":"bar","type":{"kind":"array","length":3,"type":{"kind":"string","length":5}}},{"name":"baz","type":{"kind":"field"}}]}},{"name":"bar","type":{"kind":"array","length":3,"type":{"kind":"struct","path":"MyStruct","fields":[{"name":"foo","type":{"kind":"boolean"}},{"name":"bar","type":{"kind":"array","length":3,"type":{"kind":"string","length":5}}},{"name":"baz","type":{"kind":"field"}}]}}},{"name":"baz","type":{"kind":"integer","sign":"unsigned","width":64}}]},"visibility":"private"},{"name":"string","type":{"kind":"string","length":5},"visibility":"private"}],"param_witnesses":{"array":[{"start":2,"end":7}],"my_struct":[{"start":7,"end":76}],"string":[{"start":76,"end":81}],"x":[{"start":0,"end":1}],"y":[{"start":1,"end":2}]},"return_type":{"abi_type":{"kind":"tuple","fields":[{"kind":"integer","sign":"unsigned","width":64},{"kind":"integer","sign":"unsigned","width":64},{"kind":"struct","path":"MyStruct","fields":[{"name":"foo","type":{"kind":"boolean"}},{"name":"bar","type":{"kind":"array","length":3,"type":{"kind":"string","length":5}}},{"name":"baz","type":{"kind":"field"}}]}]},"visibility":"private"},"return_witnesses":[83,84,85,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]},"bytecode":"H4sIAAAAAAAA/9Wbe3PbRBTFV3Gabiw7cZynQwFT2vIsSJZt2byahlLKo9CkhP+T2B46A6TT8dBPxvcjVw+4OV0sdXy1I++MR3skZ+9PR2tptdl7opT6VcXFufwsJfUDph3Q9NFMV0Avg74GegW0Br0KugraBV0DXQe9BnoddAP0Bugm6E3QW6C3Qe+A3kvqqW7B8X3Qb4C+AfpN0G+Bfht0G/Q7oG+Cfhf0LdC3Qd8B/R7oD+D8P4TjH4H+GPRd0J+A/hS0B9oH3QEdgO6C7oHugw5BD0B/Buf/ORz/AvSXoL8CfQ/0Aej7oA9Bfw36AehvQD8E/S3oR6C/A/1DwsiLk2zT/d58xb/O2gq8frc7DjtjP/BPvc7wbNDzur2z/sAf+L1Bb9QZBMF40B2Ew7Nh6A39bjD2J71hMEkaK5LzRwHO8YTKqFDOx2Kc55MiOX+S4xwXyfmzHOeoSM4ncpznFcZXJLNgW//+pv5Otppxt6XidD2fxkTRwMng0RI7lm4prpseY/vpvreS7KJxjbN8tS3irzDfDyT4L4tmcdti7XZ6Ljt1xbgVXIe0rLDzvCbO4/nFnGfs33XGLtNueJa2l5Zl8KnOjvP4ugDvHBY3bZtfL4wt50OHhnjRe8UsH1YNLKuWfVg1xJbzoR/1h2qGD1UDS9WyD1VDbEEfhtSGm+GDa2BxLfvgGmIL+hCNcWoZPtQMLDXLPtQMsQV96KbnOsuHuoGlbtkHzpiXV5eAV0NdJnZwSm2sZfiwZmBZs+wDZ8zLq0vAq6EuEzukKZRoHm6WD+sGlnXLPnDGvLxuCXg11GVihzQ1Fr1XzPKhYWBpWPaBM+blrZaAV0NdJnafpiSjee1ZPmwYWDYs+8AZ8/K6JeDVUJeJ3Y/uk80MH5oGlqZlHzhjXt7agvFWF4zXLQGvhrpM7DAar29m+LBpYNm07ANnzMu7sWC8jQXjLYO/GuoyscNonmsrw4ctA8uWZR84Y15eXQJeDXWZ2J1zamM7w4dtA8u2ZR84Y15eXQJeDXWZ2GE0rt7J8GHHwLJj2QfOmJe3umC8tQXjbZaAV0NdJnaflryo3Qwfdg0su5Z94Ix5efWC8dZLwKuhLhM7oKVX0Xq6WT7sGVj2LPuwZ4gt6EP0PtTK8KFlYGlZ9qFliC34PI7Wwuxn+LBvYNm37EMaj56x6TqF5y+e/TlVUPjihwqrpws/HPad24bvmhZPOEzzv1lVhgU6B8nWm6/4FQatDCciFYe3Ne/KvzlWyPi4w3DqYm1zX4+S7bH6r7etGPxOi8P2UWc5hGPp1mF/f6iudkD8jvM/7bgGjoZhX2GdMD3ZY/XqEtRl4Vi8LcmO+JptedCWfyTIdayK+YE4cC3m5TySu65XOCsF9hlvvuLP0Wde8e+poH/pb5C3WdR1/0WOOyiS80S4Dx0B61LSV+keR8MNeiDQcr90mRkNQ+jmTK/Q6RIE+vct3ZxpupReVWm6kKaeaBqDXtloOE1DSRpG0XCG0lQoNYXSUSgFhdJO2ipOL7mp4jSSWyoecdxRcVrI+ypOB6EUEEr7oFSPuypO6aA0Dvqx0QnRvCZdABrn0v/EaNxPcy/0nklruSilgtIoKHWC0iUoReJe4ul9FT+MKM3hgYrTGR6qOG3hkYrTE75XcVoCLcd/fPmhZeS0RPsJ85FPl87jGfV76pcn6uqD7mmyvZFsT6fT8R/Pp+3pRft0NGq/fDb9rX3x1/jF5PeLl+ofXhoRQLg1AAA="};
export const is_equal_circuit: CompiledCircuit = {"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[{"start":0,"end":1}],"y":[{"start":1,"end":2}]},"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"},"return_witnesses":[4]},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"};

export async function exported_function_foo(x: u64, y: u64, array: u8[], my_struct: NestedStruct, string: string, foreignCallHandler?: ForeignCallHandler): Promise<[u64, u64, MyStruct]> {
const program = new Noir(exported_function_foo_circuit);
const args: InputMap = { x, y, array, my_struct, string };
export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise<boolean> {
const program = new Noir(is_equal_circuit);
const args: InputMap = { x, y };
const { returnValue } = await program.execute(args, foreignCallHandler);
return returnValue as [u64, u64, MyStruct];
return returnValue as boolean;
}
```

As you can see, `exported_function_foo()` is now readily available to us in Typescript, and `unexported_function()` is not.
Now the `is_equal()` function and relevant types are readily available for use in Typescript.

Unchanged files with check annotations Beta

use pedersen::pedersen;
pub(crate) use range::solve_range_opcode;
use signature::{
ecdsa::{secp256k1_prehashed, secp256r1_prehashed},

Check warning on line 30 in acvm-repo/acvm/src/pwg/blackbox/mod.rs

GitHub Actions / Code

Unknown word (prehashed)

Check warning on line 30 in acvm-repo/acvm/src/pwg/blackbox/mod.rs

GitHub Actions / Code

Unknown word (prehashed)
schnorr::schnorr_verify,
};
signature,
hashed_message: message,
output,
} => secp256k1_prehashed(

Check warning on line 159 in acvm-repo/acvm/src/pwg/blackbox/mod.rs

GitHub Actions / Code

Unknown word (prehashed)
initial_witness,
public_key_x,
public_key_y,
signature,
hashed_message: message,
output,
} => secp256r1_prehashed(

Check warning on line 173 in acvm-repo/acvm/src/pwg/blackbox/mod.rs

GitHub Actions / Code

Unknown word (prehashed)
initial_witness,
public_key_x,
public_key_y,
let bytes = block.to_be_bytes();
blocks[i * 4..i * 4 + 4].copy_from_slice(&bytes);
}
let blocks: GenericArray<u8, sha2::digest::typenum::U64> = blocks.into();

Check warning on line 42 in acvm-repo/blackbox_solver/src/hash.rs

GitHub Actions / Code

Unknown word (typenum)
sha2::compress256(state, &[blocks]);
}
#[test]
fn sanity_check() {
// Test vectors are copied from XKCP (eXtended Keccak Code Package)

Check warning on line 61 in acvm-repo/blackbox_solver/src/hash.rs

GitHub Actions / Code

Unknown word (XKCP)
// https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt
let zero_state = [0u64; 25];
* Add support for overriding expression width ([#4117](https://github.com/noir-lang/noir/issues/4117)) ([c8026d5](https://github.com/noir-lang/noir/commit/c8026d557d535b10fe455165d6445076df7a03de))
* Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
* Allow brillig to read arrays directly from memory (https://github.com/AztecProtocol/aztec-packages/pull/4460) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
* Allow nested arrays and vectors in Brillig foreign calls (https://github.com/AztecProtocol/aztec-packages/pull/4478) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))

Check warning on line 35 in acvm-repo/CHANGELOG.md

GitHub Actions / Code

Unknown word (CMOV)
* Allow variables and stack trace inspection in the debugger ([#4184](https://github.com/noir-lang/noir/issues/4184)) ([bf263fc](https://github.com/noir-lang/noir/commit/bf263fc8d843940f328a90f6366edd2671fb2682))
* **avm:** Back in avm context with macro - refactor context (https://github.com/AztecProtocol/aztec-packages/pull/4438) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
* **aztec-nr:** Initial work for aztec public vm macro (https://github.com/AztecProtocol/aztec-packages/pull/4400) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
### ⚠ BREAKING CHANGES

Check warning on line 827 in acvm-repo/CHANGELOG.md

GitHub Actions / Code

Unknown word (reinitialization)
* move to bincode and GzEncoding for artifacts ([#436](https://github.com/noir-lang/acvm/issues/436))
### Features
### ⚠ BREAKING CHANGES
* **brillig:** Accept multiple inputs/outputs for foreign calls ([#367](https://github.com/noir-lang/acvm/issues/367))

Check warning on line 935 in acvm-repo/CHANGELOG.md

GitHub Actions / Code

Unknown word (blackboxes)
* **acvm:** Make internals of ACVM private ([#353](https://github.com/noir-lang/acvm/issues/353))

Check warning on line 936 in acvm-repo/CHANGELOG.md

GitHub Actions / Code

Unknown word (Simplifer)
### Features