Skip to content

Commit

Permalink
Merge branch 'master' into acvm-0.12.0
Browse files Browse the repository at this point in the history
* master:
  chore: enforce type on oracle callback function (#15)
  chore: add jsdoc for `execute_circuit` (#13)
  chore: add release-please action (#12)
  chore: add smoketest for build-info (#11)
  chore(ci): add eslint to CI (#10)
  feat: Enforce `WitnessMap` type in TS (#9)
  chore: add type aliases for tests (#7)
  feat: use JS naming convention for generated functions (#6)
  chore: remove unnecessary peer dependency
  • Loading branch information
TomAFrench committed May 22, 2023
2 parents 766a3e3 + 23f8c22 commit 033f45e
Show file tree
Hide file tree
Showing 17 changed files with 298 additions and 105 deletions.
54 changes: 54 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Release

on:
push:
branches:
- master

jobs:
release-please:
name: Create Release
outputs:
tag-name: ${{ steps.release.outputs.tag_name }}
runs-on: ubuntu-latest
steps:
- name: Run release-please
id: release
uses: google-github-actions/release-please-action@v3
with:
release-type: node
bump-minor-pre-major: true
bump-patch-for-minor-pre-major: true
pull-request-title-pattern: "chore: Release ${version}"
extra-files: |
Cargo.toml
update-lockfile:
name: Update lockfile
needs: [release-please]
if: ${{ needs.release-please.outputs.release-pr }}
runs-on: ubuntu-latest
steps:
- name: Checkout release branch
uses: actions/checkout@v3
with:
ref: ${{ fromJSON(needs.release-please.outputs.release-pr).headBranchName }}
token: ${{ secrets.NOIR_RELEASES_TOKEN }}

- name: Setup toolchain
uses: dtolnay/[email protected]

- name: Update lockfile
run: |
cargo update --workspace
- name: Configure git
run: |
git config user.name tomafrench
git config user.email [email protected]
- name: Commit updates
run: |
git add Cargo.lock
git commit -m 'chore: Update lockfile'
git push
23 changes: 23 additions & 0 deletions .github/workflows/typescript.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Typescript

on: [push, pull_request]

# This will cancel previous runs when a branch or PR is updated
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref || github.run_id }}
cancel-in-progress: true

jobs:
eslint:
name: Eslint
runs-on: ubuntu-latest

steps:
- name: Checkout sources
uses: actions/checkout@v3

- name: Install dependencies
uses: ./.github/actions/setup

- name: Run eslint
run: yarn lint
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "acvm-simulator"
version = "0.0.0"
version = "0.0.0" # x-release-please-version
authors = ["The Noir Team <[email protected]>"]
edition = "2021"
rust-version = "1.66"
Expand Down
2 changes: 1 addition & 1 deletion build-wasm
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ else
VERSION_APPENDIX="-NOGIT"
fi

jq -s '.[0] * .[1]' pkg/nodejs/package.json pkg/web/package.json | jq '.files = ["nodejs", "web", "package.json"]' | jq ".version += \"$VERSION_APPENDIX\"" | jq '.main = "./nodejs/" + .main | .module = "./web/" + .module | .types = "./web/" + .types | .peerDependencies = { "@noir-lang/noir-source-resolver": "1.1.1" }' | tee ./pkg/package.json
jq -s '.[0] * .[1]' pkg/nodejs/package.json pkg/web/package.json | jq '.files = ["nodejs", "web", "package.json"]' | jq ".version += \"$VERSION_APPENDIX\"" | jq '.main = "./nodejs/" + .main | .module = "./web/" + .module | .types = "./web/" + .types' | tee ./pkg/package.json

rm pkg/nodejs/package.json pkg/nodejs/README.md pkg/nodejs/.gitignore

Expand Down
2 changes: 2 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"arity",
"barretenberg",
"blackbox",
"Brillig",
"codegen",
"coeff",
"comptime",
Expand All @@ -23,6 +24,7 @@
"injective",
"interner",
"intrinsics",
"jsdoc",
"keccak",
"krate",
"lvalue",
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "nargo-wasm",
"version": "0.1.0",
"name": "acvm-simulator",
"version": "0.0.0",
"main": "index.js",
"repository": "[email protected]:TomAFrench/nargo-wasm.git",
"repository": "[email protected]:noir-lang/acvm-simulator.git",
"collaborators": [
"The Noir Team <[email protected]>"
],
Expand Down
15 changes: 8 additions & 7 deletions src/abi.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use acvm::acir::native_types::WitnessMap;
use iter_extended::{btree_map, try_btree_map};
use noirc_abi::{errors::InputParserError, input_parser::InputValue, Abi, MAIN_RETURN_NAME};
use serde::Serialize;
Expand All @@ -8,16 +9,16 @@ use wasm_bindgen::{prelude::wasm_bindgen, JsValue};

mod temp;

use crate::js_transforms::{js_map_to_witness_map, witness_map_to_js_map};
use crate::JsWitnessMap;

use self::temp::{input_value_from_json_type, JsonTypes};

#[wasm_bindgen]
#[wasm_bindgen(js_name = abiEncode)]
pub fn abi_encode(
abi: JsValue,
inputs: JsValue,
return_value: JsValue,
) -> Result<js_sys::Map, JsValue> {
) -> Result<JsWitnessMap, JsValue> {
console_error_panic_hook::set_once();
let abi: Abi = JsValueSerdeExt::into_serde(&abi).map_err(|err| err.to_string())?;
let inputs: BTreeMap<String, JsonTypes> =
Expand Down Expand Up @@ -52,15 +53,15 @@ pub fn abi_encode(

let witness_map = abi.encode(&parsed_inputs, return_value).map_err(|err| err.to_string())?;

Ok(witness_map_to_js_map(witness_map))
Ok(witness_map.into())
}

#[wasm_bindgen]
pub fn abi_decode(abi: JsValue, witness_map: js_sys::Map) -> Result<JsValue, JsValue> {
#[wasm_bindgen(js_name = abiDecode)]
pub fn abi_decode(abi: JsValue, witness_map: JsWitnessMap) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();
let abi: Abi = JsValueSerdeExt::into_serde(&abi).map_err(|err| err.to_string())?;

let witness_map = js_map_to_witness_map(witness_map);
let witness_map = WitnessMap::from(witness_map);

let (inputs, return_value) = abi.decode(&witness_map).map_err(|err| err.to_string())?;

Expand Down
46 changes: 35 additions & 11 deletions src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use acvm::{

use wasm_bindgen::{prelude::wasm_bindgen, JsValue};

use crate::js_transforms::{
field_element_to_js_string, js_map_to_witness_map, js_value_to_field_element,
witness_map_to_js_map,
use crate::{
js_transforms::{field_element_to_js_string, js_value_to_field_element},
JsWitnessMap,
};

#[derive(Default)]
Expand Down Expand Up @@ -158,15 +158,39 @@ impl PartialWitnessGenerator for SimulatedBackend {
}
}

#[wasm_bindgen(typescript_custom_section)]
const ORACLE_CALLBACK: &'static str = r#"
/**
* A callback which performs an oracle call and returns the response as an array of outputs.
* @callback OracleCallback
* @param {string} name - The identifier for the type of oracle call being performed.
* @param {string[]} inputs - An array of hex encoded inputs to the oracle call.
* @returns {Promise<string[]>} outputs - An array of hex encoded outputs containing the results of the oracle call.
*/
export type OracleCallback = (name: string, inputs: string[]) => Promise<string[]>;
"#;

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = js_sys::Function, typescript_type = "OracleCallback")]
pub type OracleCallback;
}

/// Executes an ACIR circuit to generate the solved witness from the initial witness.
///
/// @param {Uint8Array} circuit - A serialized representation of an ACIR circuit
/// @param {WitnessMap} initial_witness - The initial witness map defining all of the inputs to `circuit`..
/// @param {OracleCallback} oracle_callback - A callback to process oracle calls from the circuit.
/// @returns {WitnessMap} The solved witness calculated by executing the circuit on the provided inputs.
#[wasm_bindgen(js_name = executeCircuit, skip_jsdoc)]
pub async fn execute_circuit(
circuit: Vec<u8>,
initial_witness: js_sys::Map,
oracle_resolver: js_sys::Function,
) -> Result<js_sys::Map, JsValue> {
initial_witness: JsWitnessMap,
oracle_callback: OracleCallback,
) -> Result<JsWitnessMap, JsValue> {
console_error_panic_hook::set_once();
let circuit: Circuit = Circuit::read(&*circuit).expect("Failed to deserialize circuit");
let mut witness_map = js_map_to_witness_map(initial_witness);
let mut witness_map = WitnessMap::from(initial_witness);

let backend = SimulatedBackend::default();
let mut blocks = Blocks::default();
Expand All @@ -185,7 +209,7 @@ pub async fn execute_circuit(
// Perform all oracle queries
let oracle_call_futures: Vec<_> = required_oracle_data
.into_iter()
.map(|oracle_call| resolve_oracle(&oracle_resolver, oracle_call))
.map(|oracle_call| resolve_oracle(&oracle_callback, oracle_call))
.collect();

// Insert results into the witness map
Expand All @@ -207,7 +231,7 @@ pub async fn execute_circuit(
}
}

Ok(witness_map_to_js_map(witness_map))
Ok(witness_map.into())
}

fn insert_value(
Expand All @@ -230,7 +254,7 @@ fn insert_value(
}

async fn resolve_oracle(
oracle_resolver: &js_sys::Function,
oracle_callback: &OracleCallback,
mut unresolved_oracle_call: OracleData,
) -> Result<OracleData, String> {
// Prepare to call
Expand All @@ -244,7 +268,7 @@ async fn resolve_oracle(

// Call and await
let this = JsValue::null();
let ret_js_val = oracle_resolver
let ret_js_val = oracle_callback
.call2(&this, &name, &inputs)
.map_err(|err| format!("Error calling oracle_resolver: {}", format_js_err(err)))?;
let ret_js_prom: js_sys::Promise = ret_js_val.into();
Expand Down
49 changes: 29 additions & 20 deletions src/js_transforms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,33 @@ use acvm::{
use js_sys::JsString;
use wasm_bindgen::JsValue;

use crate::JsWitnessMap;

impl From<WitnessMap> for JsWitnessMap {
fn from(witness_map: WitnessMap) -> Self {
let js_map = JsWitnessMap::new();
for (key, value) in witness_map {
js_map.set(
&js_sys::Number::from(key.witness_index()),
&field_element_to_js_string(&value),
);
}
js_map
}
}

impl From<JsWitnessMap> for WitnessMap {
fn from(js_map: JsWitnessMap) -> Self {
let mut witness_map = WitnessMap::new();
js_map.for_each(&mut |value, key| {
let witness_index = Witness(key.as_f64().unwrap() as u32);
let witness_value = js_value_to_field_element(value).unwrap();
witness_map.insert(witness_index, witness_value);
});
witness_map
}
}

pub(crate) fn js_value_to_field_element(js_value: JsValue) -> Result<FieldElement, JsString> {
let hex_str = js_value.as_string().ok_or("failed to parse field element from non-string")?;

Expand All @@ -20,24 +47,6 @@ pub(crate) fn field_element_to_js_string(field_element: &FieldElement) -> JsStri
format!("0x{}", field_element.to_hex()).into()
}

pub(crate) fn js_map_to_witness_map(js_map: js_sys::Map) -> WitnessMap {
let mut witness_map = WitnessMap::new();
js_map.for_each(&mut |value, key| {
let witness_index = Witness(key.as_f64().unwrap() as u32);
let witness_value = js_value_to_field_element(value).unwrap();
witness_map.insert(witness_index, witness_value);
});
witness_map
}

pub(crate) fn witness_map_to_js_map(witness_map: WitnessMap) -> js_sys::Map {
let js_map = js_sys::Map::new();
for (key, value) in witness_map {
js_map.set(&js_sys::Number::from(key.witness_index()), &field_element_to_js_string(&value));
}
js_map
}

#[cfg(test)]
mod test {
use std::collections::BTreeMap;
Expand All @@ -49,7 +58,7 @@ mod test {
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;

use super::witness_map_to_js_map;
use crate::JsWitnessMap;

#[wasm_bindgen_test]
fn test_witness_map_to_js() {
Expand All @@ -60,7 +69,7 @@ mod test {
]);
let witness_map = WitnessMap::from(witness_map);

let js_map = witness_map_to_js_map(witness_map);
let js_map = JsWitnessMap::from(witness_map);

assert_eq!(js_map.get(&JsValue::from("1")), JsValue::from_str("1"));
}
Expand Down
28 changes: 26 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#![forbid(unsafe_code)]
#![warn(unused_crate_dependencies, unused_extern_crates)]
#![warn(unreachable_pub)]

use gloo_utils::format::JsValueSerdeExt;
use js_sys::Map;
use log::Level;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
Expand Down Expand Up @@ -40,8 +40,32 @@ const BUILD_INFO: BuildInfo = BuildInfo {
dirty: env!("GIT_DIRTY"),
};

#[wasm_bindgen]
#[wasm_bindgen(js_name = buildInfo)]
pub fn build_info() -> JsValue {
console_error_panic_hook::set_once();
<JsValue as JsValueSerdeExt>::from_serde(&BUILD_INFO).unwrap()
}

#[wasm_bindgen(typescript_custom_section)]
const WITNESS_MAP: &'static str = r#"
// Map from witness index to hex string value of witness.
export type WitnessMap = Map<number, string>;
"#;

// WitnessMap
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Map, js_name = "WitnessMap", typescript_type = "WitnessMap")]
#[derive(Clone, Debug, PartialEq, Eq)]
pub type JsWitnessMap;

#[wasm_bindgen(constructor, js_class = "Map")]
pub fn new() -> JsWitnessMap;

}

impl Default for JsWitnessMap {
fn default() -> Self {
Self::new()
}
}
Loading

0 comments on commit 033f45e

Please sign in to comment.