Skip to content

Commit

Permalink
Merge branch 'master' into mv/execute-program-acvm-js
Browse files Browse the repository at this point in the history
* master:
  chore: remove conditional compilation around `acvm_js` package (#4702)
  feat(docs): Documenting noir codegen (#4454)
  • Loading branch information
TomAFrench committed Apr 3, 2024
2 parents c89e0da + c5a7242 commit 3c85db5
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 40 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions acvm-repo/acvm_js/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,17 @@ repository.workspace = true
crate-type = ["cdylib"]

[dependencies]
cfg-if = "1.0.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
acvm.workspace = true
bn254_blackbox_solver = { workspace = true, optional = true }
wasm-bindgen.workspace = true
wasm-bindgen-futures.workspace = true
console_error_panic_hook.workspace = true
gloo-utils.workspace = true
js-sys.workspace = true
js-sys.workspace = true
serde.workspace = true
tracing-subscriber.workspace = true
tracing-web.workspace = true

serde = { version = "1.0.136", features = ["derive"] }
const-str = "0.5.5"

[build-dependencies]
Expand Down
2 changes: 0 additions & 2 deletions acvm-repo/acvm_js/src/black_box_solvers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ pub fn ecdsa_secp256k1_verify(
signature,
)
.unwrap()
.into()
}

/// Verifies a ECDSA signature over the secp256r1 curve.
Expand All @@ -81,5 +80,4 @@ pub fn ecdsa_secp256r1_verify(
signature,
)
.unwrap()
.into()
}
2 changes: 1 addition & 1 deletion acvm-repo/acvm_js/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ impl<'a, B: BlackBoxFunctionSolver> ProgramExecutor<'a, B> {
None => error.to_string(),
};

return Err(JsExecutionError::new(error_string.into(), call_stack).into());
return Err(JsExecutionError::new(error_string, call_stack).into());
}
ACVMStatus::RequiresForeignCall(foreign_call) => {
let result =
Expand Down
51 changes: 23 additions & 28 deletions acvm-repo/acvm_js/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
#![forbid(unsafe_code)]
#![warn(unreachable_pub)]
#![warn(clippy::semicolon_if_nothing_returned)]
#![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))]

// TODO: Absence of per package targets
// https://doc.rust-lang.org/cargo/reference/unstable.html#per-package-target
// otherwise could be reorganized to make this file more pretty.
mod black_box_solvers;
mod build_info;
mod compression;
mod execute;
mod foreign_call;
mod js_execution_error;
mod js_witness_map;
mod js_witness_stack;
mod logging;
mod public_witness;

cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
mod build_info;
mod compression;
mod execute;
mod foreign_call;
mod js_witness_map;
mod js_witness_stack;
mod logging;
mod public_witness;
mod js_execution_error;
mod black_box_solvers;

pub use black_box_solvers::{and, xor, sha256, blake2s256, keccak256, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify};
pub use build_info::build_info;
pub use compression::{compress_witness, decompress_witness};
pub use execute::{execute_circuit, execute_circuit_with_black_box_solver, create_black_box_solver};
pub use js_witness_map::JsWitnessMap;
pub use js_witness_stack::JsWitnessStack;
pub use logging::init_log_level;
pub use public_witness::{get_public_parameters_witness, get_public_witness, get_return_witness};
pub use js_execution_error::JsExecutionError;
}
}
pub use black_box_solvers::{
and, blake2s256, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccak256, sha256, xor,
};
pub use build_info::build_info;
pub use compression::{compress_witness, decompress_witness};
pub use execute::{
create_black_box_solver, execute_circuit, execute_circuit_with_black_box_solver,
};
pub use js_execution_error::JsExecutionError;
pub use js_witness_map::JsWitnessMap;
pub use js_witness_stack::JsWitnessStack;
pub use logging::init_log_level;
pub use public_witness::{get_public_parameters_witness, get_public_witness, get_return_witness};
1 change: 1 addition & 0 deletions acvm-repo/bn254_blackbox_solver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ acir.workspace = true
acvm_blackbox_solver.workspace = true
thiserror.workspace = true
num-traits.workspace = true
cfg-if = "1.0.0"

rust-embed = { version = "6.6.0", features = [
"debug-embed",
Expand Down
13 changes: 10 additions & 3 deletions acvm-repo/bn254_blackbox_solver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,17 @@ pub struct Bn254BlackBoxSolver {
}

impl Bn254BlackBoxSolver {
#[cfg(target_arch = "wasm32")]
pub async fn initialize() -> Bn254BlackBoxSolver {
let blackbox_vendor = Barretenberg::initialize().await;
Bn254BlackBoxSolver { blackbox_vendor }
// We fallback to the sync initialization of barretenberg on non-wasm targets.
// This ensures that wasm packages consuming this still build on the default target (useful for linting, etc.)
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
let blackbox_vendor = Barretenberg::initialize().await;
Bn254BlackBoxSolver { blackbox_vendor }
} else {
Bn254BlackBoxSolver::new()
}
}
}

#[cfg(not(target_arch = "wasm32"))]
Expand Down
113 changes: 113 additions & 0 deletions docs/docs/getting_started/tooling/noir_codegen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
title: Noir Codegen for TypeScript
description: Learn how to use Noir codegen to generate TypeScript bindings
keywords: [Nargo, Noir, compile, TypeScript]
sidebar_position: 2
---

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.

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

### 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 -D
```

### 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
mkdir circuits && cd circuits
nargo new --lib myNoirLib
```

## Usage

### Export ABI of specified functions

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(...
```

From your Noir library (where `Nargo.toml` is), run the following command:

```bash
nargo export
```

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=./circuits/myNoirLib
```

### Generate TypeScript bindings from exported functions

To use the `noir-codegen` package we added to the TypeScript project:

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

This creates an `exports` directory with an `index.ts` file containing all exported functions.

**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
```
## Example .nr function to .ts output
Consider a Noir library with this function:
```rust
#[export]
fn not_equal(x: Field, y: Field) -> bool {
x != y
}
```
After the export and codegen steps, you should have an `index.ts` like:
```typescript
export type Field = string;
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 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 boolean;
}
```
Now the `is_equal()` function and relevant types are readily available for use in TypeScript.

0 comments on commit 3c85db5

Please sign in to comment.