From 9394575240650f8198f727cd26329cf80a9e63c8 Mon Sep 17 00:00:00 2001 From: Tom French Date: Fri, 19 May 2023 14:15:16 +0100 Subject: [PATCH 1/9] chore: remove unnecessary peer dependency --- build-wasm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-wasm b/build-wasm index 0762d12..0934725 100755 --- a/build-wasm +++ b/build-wasm @@ -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 From 482be49121cabe3adb00c12da8e35da6519c4918 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 22 May 2023 06:37:21 +0100 Subject: [PATCH 2/9] feat: use JS naming convention for generated functions (#6) --- src/abi.rs | 4 ++-- src/execute.rs | 2 +- test/browser/abi_encode.test.ts | 6 +++--- test/browser/execute_circuit.test.ts | 14 +++++++------- test/node/abi_encode.test.ts | 6 +++--- test/node/execute_circuit.test.ts | 10 +++++----- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/abi.rs b/src/abi.rs index 6fcbf70..4c92d13 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -12,7 +12,7 @@ use crate::js_transforms::{js_map_to_witness_map, witness_map_to_js_map}; use self::temp::{input_value_from_json_type, JsonTypes}; -#[wasm_bindgen] +#[wasm_bindgen(js_name = abiEncode)] pub fn abi_encode( abi: JsValue, inputs: JsValue, @@ -55,7 +55,7 @@ pub fn abi_encode( Ok(witness_map_to_js_map(witness_map)) } -#[wasm_bindgen] +#[wasm_bindgen(js_name = abiDecode)] pub fn abi_decode(abi: JsValue, witness_map: js_sys::Map) -> Result { console_error_panic_hook::set_once(); let abi: Abi = JsValueSerdeExt::into_serde(&abi).map_err(|err| err.to_string())?; diff --git a/src/execute.rs b/src/execute.rs index ce6d34f..f9027ad 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -53,7 +53,7 @@ impl PartialWitnessGenerator for SimulatedBackend { } } -#[wasm_bindgen] +#[wasm_bindgen(js_name = executeCircuit)] pub async fn execute_circuit( circuit: Vec, initial_witness: js_sys::Map, diff --git a/test/browser/abi_encode.test.ts b/test/browser/abi_encode.test.ts index d4293ff..cf762ae 100644 --- a/test/browser/abi_encode.test.ts +++ b/test/browser/abi_encode.test.ts @@ -1,4 +1,4 @@ -import initACVMSimulator, { abi_encode, abi_decode } from "../../pkg/"; +import initACVMSimulator, { abiEncode, abiDecode } from "../../pkg/"; test("recovers original inputs when abi encoding and decoding", async () => { await initACVMSimulator(); @@ -21,10 +21,10 @@ test("recovers original inputs when abi encoding and decoding", async () => { foo: "1", bar: ["1", "2"], }; - const initial_witness: Map = abi_encode(abi, inputs, null); + const initial_witness: Map = abiEncode(abi, inputs, null); // eslint-disable-next-line @typescript-eslint/no-explicit-any const decoded_inputs: { inputs: Record; return_value: any } = - abi_decode(abi, initial_witness); + abiDecode(abi, initial_witness); expect(BigInt(decoded_inputs.inputs.foo)).toBe(BigInt(inputs.foo)); expect(BigInt(decoded_inputs.inputs.bar[0])).toBe(BigInt(inputs.bar[0])); diff --git a/test/browser/execute_circuit.test.ts b/test/browser/execute_circuit.test.ts index 2abf976..7cb3903 100644 --- a/test/browser/execute_circuit.test.ts +++ b/test/browser/execute_circuit.test.ts @@ -1,7 +1,7 @@ import initACVMSimulator, { - abi_encode, - abi_decode, - execute_circuit, + abiEncode, + abiDecode, + executeCircuit, } from "../../pkg/"; test("successfully executes circuit and extracts return value", async () => { @@ -59,8 +59,8 @@ test("successfully executes circuit and extracts return value", async () => { }; const return_witness: number = abi.return_witnesses[0]; - const initial_witness: Map = abi_encode(abi, inputs, null); - const solved_witness: Map = await execute_circuit( + const initial_witness: Map = abiEncode(abi, inputs, null); + const solved_witness: Map = await executeCircuit( bytecode, initial_witness, () => { @@ -75,7 +75,7 @@ test("successfully executes circuit and extracts return value", async () => { // Solved witness should contain expected return value expect(BigInt(solved_witness.get(return_witness) as string)).toBe(3n); - const decoded_inputs = abi_decode(abi, solved_witness); + const decoded_inputs = abiDecode(abi, solved_witness); expect(BigInt(decoded_inputs.return_value)).toBe(3n); }); @@ -138,7 +138,7 @@ test("successfully processes oracle opcodes", async () => { "0x0000000000000000000000000000000000000000000000000000000000000001" ); - const solved_witness: Map = await execute_circuit( + const solved_witness: Map = await executeCircuit( oracle_bytecode, initial_witness, async (_name: string, _inputs: string[]) => { diff --git a/test/node/abi_encode.test.ts b/test/node/abi_encode.test.ts index 6a7b393..f70afee 100644 --- a/test/node/abi_encode.test.ts +++ b/test/node/abi_encode.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "@jest/globals"; -import { abi_encode, abi_decode } from "../../pkg/"; +import { abiEncode, abiDecode } from "../../pkg/"; test("recovers original inputs when abi encoding and decoding", () => { // TODO use ts-rs to get ABI type bindings. @@ -20,10 +20,10 @@ test("recovers original inputs when abi encoding and decoding", () => { foo: "1", bar: ["1", "2"], }; - const initial_witness: Map = abi_encode(abi, inputs, null); + const initial_witness: Map = abiEncode(abi, inputs, null); // eslint-disable-next-line @typescript-eslint/no-explicit-any const decoded_inputs: { inputs: Record; return_value: any } = - abi_decode(abi, initial_witness); + abiDecode(abi, initial_witness); expect(BigInt(decoded_inputs.inputs.foo)).toBe(BigInt(inputs.foo)); expect(BigInt(decoded_inputs.inputs.bar[0])).toBe(BigInt(inputs.bar[0])); diff --git a/test/node/execute_circuit.test.ts b/test/node/execute_circuit.test.ts index f9b404a..638e305 100644 --- a/test/node/execute_circuit.test.ts +++ b/test/node/execute_circuit.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "@jest/globals"; -import { abi_encode, abi_decode, execute_circuit } from "../../pkg/"; +import { abiEncode, abiDecode, executeCircuit } from "../../pkg/"; test("successfully executes circuit and extracts return value", async () => { // Noir program which enforces that x != y and returns x + y. @@ -54,8 +54,8 @@ test("successfully executes circuit and extracts return value", async () => { }; const return_witness: number = abi.return_witnesses[0]; - const initial_witness: Map = abi_encode(abi, inputs, null); - const solved_witness: Map = await execute_circuit( + const initial_witness: Map = abiEncode(abi, inputs, null); + const solved_witness: Map = await executeCircuit( bytecode, initial_witness, () => { @@ -70,7 +70,7 @@ test("successfully executes circuit and extracts return value", async () => { // Solved witness should contain expected return value expect(BigInt(solved_witness.get(return_witness) as string)).toBe(3n); - const decoded_inputs = abi_decode(abi, solved_witness); + const decoded_inputs = abiDecode(abi, solved_witness); expect(BigInt(decoded_inputs.return_value)).toBe(3n); }); @@ -131,7 +131,7 @@ test("successfully processes oracle opcodes", async () => { "0x0000000000000000000000000000000000000000000000000000000000000001" ); - const solved_witness: Map = await execute_circuit( + const solved_witness: Map = await executeCircuit( oracle_bytecode, initial_witness, async (_name: string, _inputs: string[]) => { From 6b8c51590ef4e052dfc407139bdad59842bd90a1 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 22 May 2023 06:46:44 +0100 Subject: [PATCH 3/9] chore: add type aliases for tests (#7) --- test/browser/abi_encode.test.ts | 6 +++--- test/browser/execute_circuit.test.ts | 9 +++++---- test/node/abi_encode.test.ts | 7 +++---- test/node/execute_circuit.test.ts | 9 +++++---- test/types.ts | 5 +++++ 5 files changed, 21 insertions(+), 15 deletions(-) create mode 100644 test/types.ts diff --git a/test/browser/abi_encode.test.ts b/test/browser/abi_encode.test.ts index cf762ae..bf2c1a3 100644 --- a/test/browser/abi_encode.test.ts +++ b/test/browser/abi_encode.test.ts @@ -1,4 +1,5 @@ import initACVMSimulator, { abiEncode, abiDecode } from "../../pkg/"; +import { DecodedInputs, WitnessMap } from "../types"; test("recovers original inputs when abi encoding and decoding", async () => { await initACVMSimulator(); @@ -21,10 +22,9 @@ test("recovers original inputs when abi encoding and decoding", async () => { foo: "1", bar: ["1", "2"], }; - const initial_witness: Map = abiEncode(abi, inputs, null); + const initial_witness: WitnessMap = abiEncode(abi, inputs, null); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const decoded_inputs: { inputs: Record; return_value: any } = - abiDecode(abi, initial_witness); + const decoded_inputs: DecodedInputs = abiDecode(abi, initial_witness); expect(BigInt(decoded_inputs.inputs.foo)).toBe(BigInt(inputs.foo)); expect(BigInt(decoded_inputs.inputs.bar[0])).toBe(BigInt(inputs.bar[0])); diff --git a/test/browser/execute_circuit.test.ts b/test/browser/execute_circuit.test.ts index 7cb3903..05d41ea 100644 --- a/test/browser/execute_circuit.test.ts +++ b/test/browser/execute_circuit.test.ts @@ -3,6 +3,7 @@ import initACVMSimulator, { abiDecode, executeCircuit, } from "../../pkg/"; +import { WitnessMap } from "../types"; test("successfully executes circuit and extracts return value", async () => { await initACVMSimulator(); @@ -59,8 +60,8 @@ test("successfully executes circuit and extracts return value", async () => { }; const return_witness: number = abi.return_witnesses[0]; - const initial_witness: Map = abiEncode(abi, inputs, null); - const solved_witness: Map = await executeCircuit( + const initial_witness: WitnessMap = abiEncode(abi, inputs, null); + const solved_witness: WitnessMap = await executeCircuit( bytecode, initial_witness, () => { @@ -128,7 +129,7 @@ test("successfully processes oracle opcodes", async () => { 0, 0, 0, 0, 0, 0, 0, 0, ]); - const initial_witness: Map = new Map(); + const initial_witness: WitnessMap = new Map(); initial_witness.set( 1, "0x0000000000000000000000000000000000000000000000000000000000000001" @@ -138,7 +139,7 @@ test("successfully processes oracle opcodes", async () => { "0x0000000000000000000000000000000000000000000000000000000000000001" ); - const solved_witness: Map = await executeCircuit( + const solved_witness: WitnessMap = await executeCircuit( oracle_bytecode, initial_witness, async (_name: string, _inputs: string[]) => { diff --git a/test/node/abi_encode.test.ts b/test/node/abi_encode.test.ts index f70afee..c9c9d46 100644 --- a/test/node/abi_encode.test.ts +++ b/test/node/abi_encode.test.ts @@ -1,5 +1,6 @@ import { expect, test } from "@jest/globals"; import { abiEncode, abiDecode } from "../../pkg/"; +import { DecodedInputs, WitnessMap } from "../types"; test("recovers original inputs when abi encoding and decoding", () => { // TODO use ts-rs to get ABI type bindings. @@ -20,10 +21,8 @@ test("recovers original inputs when abi encoding and decoding", () => { foo: "1", bar: ["1", "2"], }; - const initial_witness: Map = abiEncode(abi, inputs, null); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const decoded_inputs: { inputs: Record; return_value: any } = - abiDecode(abi, initial_witness); + const initial_witness: WitnessMap = abiEncode(abi, inputs, null); + const decoded_inputs: DecodedInputs = abiDecode(abi, initial_witness); expect(BigInt(decoded_inputs.inputs.foo)).toBe(BigInt(inputs.foo)); expect(BigInt(decoded_inputs.inputs.bar[0])).toBe(BigInt(inputs.bar[0])); diff --git a/test/node/execute_circuit.test.ts b/test/node/execute_circuit.test.ts index 638e305..dae01fc 100644 --- a/test/node/execute_circuit.test.ts +++ b/test/node/execute_circuit.test.ts @@ -1,5 +1,6 @@ import { expect, test } from "@jest/globals"; import { abiEncode, abiDecode, executeCircuit } from "../../pkg/"; +import { WitnessMap } from "../types"; test("successfully executes circuit and extracts return value", async () => { // Noir program which enforces that x != y and returns x + y. @@ -54,8 +55,8 @@ test("successfully executes circuit and extracts return value", async () => { }; const return_witness: number = abi.return_witnesses[0]; - const initial_witness: Map = abiEncode(abi, inputs, null); - const solved_witness: Map = await executeCircuit( + const initial_witness: WitnessMap = abiEncode(abi, inputs, null); + const solved_witness: WitnessMap = await executeCircuit( bytecode, initial_witness, () => { @@ -121,7 +122,7 @@ test("successfully processes oracle opcodes", async () => { 0, 0, 0, 0, 0, 0, 0, 0, ]); - const initial_witness: Map = new Map(); + const initial_witness: WitnessMap = new Map(); initial_witness.set( 1, "0x0000000000000000000000000000000000000000000000000000000000000001" @@ -131,7 +132,7 @@ test("successfully processes oracle opcodes", async () => { "0x0000000000000000000000000000000000000000000000000000000000000001" ); - const solved_witness: Map = await executeCircuit( + const solved_witness: WitnessMap = await executeCircuit( oracle_bytecode, initial_witness, async (_name: string, _inputs: string[]) => { diff --git a/test/types.ts b/test/types.ts new file mode 100644 index 0000000..0c51a4e --- /dev/null +++ b/test/types.ts @@ -0,0 +1,5 @@ +// Map from witness index to hex string value of witness. +export type WitnessMap = Map; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type DecodedInputs = { inputs: Record; return_value: any }; From 26b909f39d8678981bf9762a8319bbce117ddbb1 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 22 May 2023 07:50:20 +0100 Subject: [PATCH 4/9] feat: Enforce `WitnessMap` type in TS (#9) chore: implement `From` between JS and Rust witness maps --- src/abi.rs | 11 ++-- src/execute.rs | 14 ++--- src/js_transforms.rs | 49 +++++++++------- src/lib.rs | 84 +++++++++++++++++++++++++++- test/browser/abi_encode.test.ts | 8 ++- test/browser/execute_circuit.test.ts | 2 +- test/node/abi_encode.test.ts | 4 +- test/node/execute_circuit.test.ts | 3 +- test/types.ts | 3 - 9 files changed, 135 insertions(+), 43 deletions(-) diff --git a/src/abi.rs b/src/abi.rs index 4c92d13..a772a43 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -1,3 +1,4 @@ +use acvm::{acir::native_types::Witness, FieldElement}; use iter_extended::{btree_map, try_btree_map}; use noirc_abi::{errors::InputParserError, input_parser::InputValue, Abi, MAIN_RETURN_NAME}; use serde::Serialize; @@ -8,7 +9,7 @@ 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}; @@ -17,7 +18,7 @@ pub fn abi_encode( abi: JsValue, inputs: JsValue, return_value: JsValue, -) -> Result { +) -> Result { console_error_panic_hook::set_once(); let abi: Abi = JsValueSerdeExt::into_serde(&abi).map_err(|err| err.to_string())?; let inputs: BTreeMap = @@ -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(js_name = abiDecode)] -pub fn abi_decode(abi: JsValue, witness_map: js_sys::Map) -> Result { +pub fn abi_decode(abi: JsValue, witness_map: JsWitnessMap) -> Result { 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: BTreeMap = witness_map.into(); let (inputs, return_value) = abi.decode(&witness_map).map_err(|err| err.to_string())?; diff --git a/src/execute.rs b/src/execute.rs index f9027ad..09ad7ec 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -15,9 +15,9 @@ use std::collections::BTreeMap; 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, }; struct SimulatedBackend; @@ -56,12 +56,12 @@ impl PartialWitnessGenerator for SimulatedBackend { #[wasm_bindgen(js_name = executeCircuit)] pub async fn execute_circuit( circuit: Vec, - initial_witness: js_sys::Map, + initial_witness: JsWitnessMap, oracle_resolver: js_sys::Function, -) -> Result { +) -> Result { 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: BTreeMap = initial_witness.into(); let mut blocks = Blocks::default(); let mut opcodes = circuit.opcodes; @@ -102,7 +102,7 @@ pub async fn execute_circuit( } } - Ok(witness_map_to_js_map(witness_map)) + Ok(witness_map.into()) } fn insert_value( diff --git a/src/js_transforms.rs b/src/js_transforms.rs index 1a50567..8149477 100644 --- a/src/js_transforms.rs +++ b/src/js_transforms.rs @@ -3,6 +3,33 @@ use js_sys::JsString; use std::collections::BTreeMap; use wasm_bindgen::JsValue; +use crate::JsWitnessMap; + +impl From> for JsWitnessMap { + fn from(witness_map: BTreeMap) -> 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 for BTreeMap { + fn from(js_map: JsWitnessMap) -> Self { + let mut witness_map: BTreeMap = BTreeMap::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 { let hex_str = js_value.as_string().ok_or("failed to parse field element from non-string")?; @@ -18,24 +45,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) -> BTreeMap { - let mut witness_map: BTreeMap = BTreeMap::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: BTreeMap) -> 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; @@ -44,7 +53,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() { @@ -54,7 +63,7 @@ mod test { (Witness(3), -FieldElement::one()), ]); - 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")); } diff --git a/src/lib.rs b/src/lib.rs index 7d570d2..4e3250b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; @@ -45,3 +45,85 @@ pub fn build_info() -> JsValue { console_error_panic_hook::set_once(); ::from_serde(&BUILD_INFO).unwrap() } + +#[wasm_bindgen(typescript_custom_section)] +const TS_APPEND_CONTENT: &'static str = r#" +// Map from witness index to hex string value of witness. +export type WitnessMap = Map; + +"#; + +// WitnessMap +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Map, js_name = "WitnessMap", typescript_type = "WitnessMap")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type JsWitnessMap; + + /// The `clear()` method removes all elements from a Map object. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear) + #[wasm_bindgen(method, js_class = "Map")] + pub fn clear(this: &JsWitnessMap); + + /// The `delete()` method removes the specified element from a Map object. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete) + #[wasm_bindgen(method, js_class = "Map")] + pub fn delete(this: &JsWitnessMap, key: &JsValue) -> bool; + + /// The `forEach()` method executes a provided function once per each + /// key/value pair in the Map object, in insertion order. + /// Note that in Javascript land the `Key` and `Value` are reversed compared to normal expectations: + /// # Examples + /// ``` + /// let js_map = Map::new(); + /// js_map.for_each(&mut |value, key| { + /// // Do something here... + /// }) + /// ``` + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach) + #[wasm_bindgen(method, js_class = "Map", js_name = forEach)] + pub fn for_each(this: &JsWitnessMap, callback: &mut dyn FnMut(JsValue, JsValue)); + + /// The `get()` method returns a specified element from a Map object. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) + #[wasm_bindgen(method, js_class = "Map")] + pub fn get(this: &JsWitnessMap, key: &JsValue) -> JsValue; + + /// The `has()` method returns a boolean indicating whether an element with + /// the specified key exists or not. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) + #[wasm_bindgen(method, js_class = "Map")] + pub fn has(this: &JsWitnessMap, key: &JsValue) -> bool; + + /// The Map object holds key-value pairs. Any value (both objects and + /// primitive values) maybe used as either a key or a value. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) + #[wasm_bindgen(constructor, js_class = "Map")] + pub fn new() -> JsWitnessMap; + + /// The `set()` method adds or updates an element with a specified key + /// and value to a Map object. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set) + #[wasm_bindgen(method, js_class = "Map")] + pub fn set(this: &JsWitnessMap, key: &JsValue, value: &JsValue) -> Map; + + /// The value of size is an integer representing how many entries + /// the Map object has. A set accessor function for size is undefined; + /// you can not change this property. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size) + #[wasm_bindgen(method, js_class = "Map", getter, structural)] + pub fn size(this: &JsWitnessMap) -> u32; +} + +impl Default for JsWitnessMap { + fn default() -> Self { + Self::new() + } +} diff --git a/test/browser/abi_encode.test.ts b/test/browser/abi_encode.test.ts index bf2c1a3..90ab245 100644 --- a/test/browser/abi_encode.test.ts +++ b/test/browser/abi_encode.test.ts @@ -1,5 +1,9 @@ -import initACVMSimulator, { abiEncode, abiDecode } from "../../pkg/"; -import { DecodedInputs, WitnessMap } from "../types"; +import initACVMSimulator, { + abiEncode, + abiDecode, + WitnessMap, +} from "../../pkg/"; +import { DecodedInputs } from "../types"; test("recovers original inputs when abi encoding and decoding", async () => { await initACVMSimulator(); diff --git a/test/browser/execute_circuit.test.ts b/test/browser/execute_circuit.test.ts index 05d41ea..8d4a1a9 100644 --- a/test/browser/execute_circuit.test.ts +++ b/test/browser/execute_circuit.test.ts @@ -2,8 +2,8 @@ import initACVMSimulator, { abiEncode, abiDecode, executeCircuit, + WitnessMap, } from "../../pkg/"; -import { WitnessMap } from "../types"; test("successfully executes circuit and extracts return value", async () => { await initACVMSimulator(); diff --git a/test/node/abi_encode.test.ts b/test/node/abi_encode.test.ts index c9c9d46..04787f0 100644 --- a/test/node/abi_encode.test.ts +++ b/test/node/abi_encode.test.ts @@ -1,6 +1,6 @@ import { expect, test } from "@jest/globals"; -import { abiEncode, abiDecode } from "../../pkg/"; -import { DecodedInputs, WitnessMap } from "../types"; +import { abiEncode, abiDecode, WitnessMap } from "../../pkg/"; +import { DecodedInputs } from "../types"; test("recovers original inputs when abi encoding and decoding", () => { // TODO use ts-rs to get ABI type bindings. diff --git a/test/node/execute_circuit.test.ts b/test/node/execute_circuit.test.ts index dae01fc..fcbf77f 100644 --- a/test/node/execute_circuit.test.ts +++ b/test/node/execute_circuit.test.ts @@ -1,6 +1,5 @@ import { expect, test } from "@jest/globals"; -import { abiEncode, abiDecode, executeCircuit } from "../../pkg/"; -import { WitnessMap } from "../types"; +import { abiEncode, abiDecode, executeCircuit, WitnessMap } from "../../pkg/"; test("successfully executes circuit and extracts return value", async () => { // Noir program which enforces that x != y and returns x + y. diff --git a/test/types.ts b/test/types.ts index 0c51a4e..3a72544 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,5 +1,2 @@ -// Map from witness index to hex string value of witness. -export type WitnessMap = Map; - // eslint-disable-next-line @typescript-eslint/no-explicit-any export type DecodedInputs = { inputs: Record; return_value: any }; From 163d280745dc395c597d5ff58f53006c655891f6 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 22 May 2023 08:49:48 +0100 Subject: [PATCH 5/9] chore(ci): add eslint to CI (#10) --- .github/workflows/typescript.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/typescript.yml diff --git a/.github/workflows/typescript.yml b/.github/workflows/typescript.yml new file mode 100644 index 0000000..394fb59 --- /dev/null +++ b/.github/workflows/typescript.yml @@ -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 From fd1d7e8448078e0ca09194d425efb8fa4556c61d Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 22 May 2023 09:10:19 +0100 Subject: [PATCH 6/9] chore: add smoketest for build-info (#11) --- package.json | 6 +++--- src/lib.rs | 2 +- test/node/build_info.test.ts | 17 ++++++++++++++++ yarn.lock | 38 ++++++++++++++++++------------------ 4 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 test/node/build_info.test.ts diff --git a/package.json b/package.json index 7bc924c..f537d3d 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "nargo-wasm", - "version": "0.1.0", + "name": "acvm-simulator", + "version": "0.0.0", "main": "index.js", - "repository": "git@github.com:TomAFrench/nargo-wasm.git", + "repository": "git@github.com:noir-lang/acvm-simulator.git", "collaborators": [ "The Noir Team " ], diff --git a/src/lib.rs b/src/lib.rs index 4e3250b..e298490 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,7 +40,7 @@ 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(); ::from_serde(&BUILD_INFO).unwrap() diff --git a/test/node/build_info.test.ts b/test/node/build_info.test.ts new file mode 100644 index 0000000..67bea62 --- /dev/null +++ b/test/node/build_info.test.ts @@ -0,0 +1,17 @@ +import { expect, test } from "@jest/globals"; +import { buildInfo } from "../../pkg/"; +import child_process from "child_process"; +import pkg from "../../package.json"; + +test("returns the correct build into", () => { + const info = buildInfo(); + + // TODO: enforce that `package.json` and `Cargo.toml` are consistent. + expect(info.version).toBe(pkg.version); + + const revision = child_process + .execSync("git rev-parse HEAD") + .toString() + .trim(); + expect(info.git_hash).toBe(revision); +}); diff --git a/yarn.lock b/yarn.lock index b6897d4..e15fa96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1839,6 +1839,25 @@ __metadata: languageName: node linkType: hard +"acvm-simulator@workspace:.": + version: 0.0.0-use.local + resolution: "acvm-simulator@workspace:." + dependencies: + "@types/jest": ^29.5.1 + "@typescript-eslint/eslint-plugin": ^5.59.5 + "@typescript-eslint/parser": ^5.59.5 + "@web/dev-server-esbuild": ^0.3.6 + "@web/test-runner": ^0.15.3 + eslint: ^8.40.0 + eslint-plugin-prettier: ^4.2.1 + jest: ^29.5.0 + jest-browser-globals: ^25.1.0-beta + prettier: ^2.8.8 + ts-jest: ^29.1.0 + typescript: ^5.0.4 + languageName: unknown + linkType: soft + "agent-base@npm:6, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" @@ -4997,25 +5016,6 @@ __metadata: languageName: node linkType: hard -"nargo-wasm@workspace:.": - version: 0.0.0-use.local - resolution: "nargo-wasm@workspace:." - dependencies: - "@types/jest": ^29.5.1 - "@typescript-eslint/eslint-plugin": ^5.59.5 - "@typescript-eslint/parser": ^5.59.5 - "@web/dev-server-esbuild": ^0.3.6 - "@web/test-runner": ^0.15.3 - eslint: ^8.40.0 - eslint-plugin-prettier: ^4.2.1 - jest: ^29.5.0 - jest-browser-globals: ^25.1.0-beta - prettier: ^2.8.8 - ts-jest: ^29.1.0 - typescript: ^5.0.4 - languageName: unknown - linkType: soft - "natural-compare-lite@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare-lite@npm:1.4.0" From 26313ace505aa0456b0d4ca0dbdeeeb4052fef00 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 22 May 2023 09:37:26 +0100 Subject: [PATCH 7/9] chore: add release-please action (#12) --- .github/workflows/release.yml | 54 +++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..88fb8a8 --- /dev/null +++ b/.github/workflows/release.yml @@ -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/rust-toolchain@1.66.0 + + - name: Update lockfile + run: | + cargo update --workspace + + - name: Configure git + run: | + git config user.name tomafrench + git config user.email tomfrench@aztecprotocol.com + + - name: Commit updates + run: | + git add Cargo.lock + git commit -m 'chore: Update lockfile' + git push diff --git a/Cargo.toml b/Cargo.toml index 21e2e55..2f426ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "acvm-simulator" -version = "0.0.0" +version = "0.0.0" # x-release-please-version authors = ["The Noir Team "] edition = "2021" rust-version = "1.66" From e54dc67cc36269035077966c2f449b1fb3eece24 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 22 May 2023 12:14:53 +0100 Subject: [PATCH 8/9] chore: add jsdoc for `execute_circuit` (#13) --- cspell.json | 2 ++ src/execute.rs | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/cspell.json b/cspell.json index 43bf691..d46108c 100644 --- a/cspell.json +++ b/cspell.json @@ -9,6 +9,7 @@ "arity", "barretenberg", "blackbox", + "Brillig", "codegen", "coeff", "comptime", @@ -23,6 +24,7 @@ "injective", "interner", "intrinsics", + "jsdoc", "keccak", "krate", "lvalue", diff --git a/src/execute.rs b/src/execute.rs index 09ad7ec..47c1c63 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -53,11 +53,29 @@ impl PartialWitnessGenerator for SimulatedBackend { } } -#[wasm_bindgen(js_name = executeCircuit)] +// TODO: enforce this type, this is reliant on Brillig (see https://github.com/noir-lang/acvm/issues/298) +#[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} outputs - An array of hex encoded outputs containing the results of the oracle call. + */ +"#; + +/// 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, initial_witness: JsWitnessMap, - oracle_resolver: js_sys::Function, + oracle_callback: js_sys::Function, ) -> Result { console_error_panic_hook::set_once(); let circuit: Circuit = Circuit::read(&*circuit).expect("Failed to deserialize circuit"); @@ -80,7 +98,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 From 23f8c22d626a70a445d9262f3641f9322ab7d52a Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 22 May 2023 12:47:26 +0100 Subject: [PATCH 9/9] chore: enforce type on oracle callback function (#15) --- src/execute.rs | 14 +++++-- src/lib.rs | 58 ---------------------------- test/browser/execute_circuit.test.ts | 32 ++++++++++----- test/node/execute_circuit.test.ts | 39 ++++++++++++++----- 4 files changed, 62 insertions(+), 81 deletions(-) diff --git a/src/execute.rs b/src/execute.rs index 47c1c63..cfdcd85 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -53,7 +53,6 @@ impl PartialWitnessGenerator for SimulatedBackend { } } -// TODO: enforce this type, this is reliant on Brillig (see https://github.com/noir-lang/acvm/issues/298) #[wasm_bindgen(typescript_custom_section)] const ORACLE_CALLBACK: &'static str = r#" /** @@ -63,8 +62,15 @@ const ORACLE_CALLBACK: &'static str = r#" * @param {string[]} inputs - An array of hex encoded inputs to the oracle call. * @returns {Promise} outputs - An array of hex encoded outputs containing the results of the oracle call. */ +export type OracleCallback = (name: string, inputs: string[]) => Promise; "#; +#[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 @@ -75,7 +81,7 @@ const ORACLE_CALLBACK: &'static str = r#" pub async fn execute_circuit( circuit: Vec, initial_witness: JsWitnessMap, - oracle_callback: js_sys::Function, + oracle_callback: OracleCallback, ) -> Result { console_error_panic_hook::set_once(); let circuit: Circuit = Circuit::read(&*circuit).expect("Failed to deserialize circuit"); @@ -143,7 +149,7 @@ fn insert_value( } async fn resolve_oracle( - oracle_resolver: &js_sys::Function, + oracle_callback: &OracleCallback, mut unresolved_oracle_call: OracleData, ) -> Result { // Prepare to call @@ -157,7 +163,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(); diff --git a/src/lib.rs b/src/lib.rs index e298490..30ddb10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,6 @@ pub fn build_info() -> JsValue { const TS_APPEND_CONTENT: &'static str = r#" // Map from witness index to hex string value of witness. export type WitnessMap = Map; - "#; // WitnessMap @@ -60,66 +59,9 @@ extern "C" { #[derive(Clone, Debug, PartialEq, Eq)] pub type JsWitnessMap; - /// The `clear()` method removes all elements from a Map object. - /// - /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear) - #[wasm_bindgen(method, js_class = "Map")] - pub fn clear(this: &JsWitnessMap); - - /// The `delete()` method removes the specified element from a Map object. - /// - /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete) - #[wasm_bindgen(method, js_class = "Map")] - pub fn delete(this: &JsWitnessMap, key: &JsValue) -> bool; - - /// The `forEach()` method executes a provided function once per each - /// key/value pair in the Map object, in insertion order. - /// Note that in Javascript land the `Key` and `Value` are reversed compared to normal expectations: - /// # Examples - /// ``` - /// let js_map = Map::new(); - /// js_map.for_each(&mut |value, key| { - /// // Do something here... - /// }) - /// ``` - /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach) - #[wasm_bindgen(method, js_class = "Map", js_name = forEach)] - pub fn for_each(this: &JsWitnessMap, callback: &mut dyn FnMut(JsValue, JsValue)); - - /// The `get()` method returns a specified element from a Map object. - /// - /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) - #[wasm_bindgen(method, js_class = "Map")] - pub fn get(this: &JsWitnessMap, key: &JsValue) -> JsValue; - - /// The `has()` method returns a boolean indicating whether an element with - /// the specified key exists or not. - /// - /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) - #[wasm_bindgen(method, js_class = "Map")] - pub fn has(this: &JsWitnessMap, key: &JsValue) -> bool; - - /// The Map object holds key-value pairs. Any value (both objects and - /// primitive values) maybe used as either a key or a value. - /// - /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) #[wasm_bindgen(constructor, js_class = "Map")] pub fn new() -> JsWitnessMap; - /// The `set()` method adds or updates an element with a specified key - /// and value to a Map object. - /// - /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set) - #[wasm_bindgen(method, js_class = "Map")] - pub fn set(this: &JsWitnessMap, key: &JsValue, value: &JsValue) -> Map; - - /// The value of size is an integer representing how many entries - /// the Map object has. A set accessor function for size is undefined; - /// you can not change this property. - /// - /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size) - #[wasm_bindgen(method, js_class = "Map", getter, structural)] - pub fn size(this: &JsWitnessMap) -> u32; } impl Default for JsWitnessMap { diff --git a/test/browser/execute_circuit.test.ts b/test/browser/execute_circuit.test.ts index 8d4a1a9..f50fb99 100644 --- a/test/browser/execute_circuit.test.ts +++ b/test/browser/execute_circuit.test.ts @@ -3,6 +3,7 @@ import initACVMSimulator, { abiDecode, executeCircuit, WitnessMap, + OracleCallback, } from "../../pkg/"; test("successfully executes circuit and extracts return value", async () => { @@ -139,20 +140,33 @@ test("successfully processes oracle opcodes", async () => { "0x0000000000000000000000000000000000000000000000000000000000000001" ); + let observedName = ""; + let observedInputs: string[] = []; + const oracleCallback: OracleCallback = async ( + name: string, + inputs: string[] + ) => { + // Throwing inside the oracle callback causes a timeout so we log the observed values + // and defer the check against expected values until after the execution is complete. + observedName = name; + observedInputs = inputs; + + // Witness(1) + Witness(2) = 1 + 1 = 2 + return ["0x02"]; + }; const solved_witness: WitnessMap = await executeCircuit( oracle_bytecode, initial_witness, - async (_name: string, _inputs: string[]) => { - // We cannot use jest matchers here (or write to a variable in the outside scope) so cannot test that - // the values for `name` and `inputs` are correct, we can `console.log` them however. - // console.log(name) - // console.log(inputs) - - // Witness(1) + Witness(2) = 1 + 1 = 2 - return ["0x02"]; - } + oracleCallback ); + // Check that expected values were passed to oracle callback. + expect(observedName).toBe("example_oracle"); + expect(observedInputs).toStrictEqual([ + initial_witness.get(1) as string, + initial_witness.get(2) as string, + ]); + // If incorrect value is written into circuit then execution should halt due to unsatisfied constraint in // arithmetic opcode. Nevertheless, check that returned value was inserted correctly. expect(solved_witness.get(3) as string).toBe( diff --git a/test/node/execute_circuit.test.ts b/test/node/execute_circuit.test.ts index fcbf77f..bcb0218 100644 --- a/test/node/execute_circuit.test.ts +++ b/test/node/execute_circuit.test.ts @@ -1,5 +1,11 @@ import { expect, test } from "@jest/globals"; -import { abiEncode, abiDecode, executeCircuit, WitnessMap } from "../../pkg/"; +import { + abiEncode, + abiDecode, + executeCircuit, + WitnessMap, + OracleCallback, +} from "../../pkg/"; test("successfully executes circuit and extracts return value", async () => { // Noir program which enforces that x != y and returns x + y. @@ -131,20 +137,33 @@ test("successfully processes oracle opcodes", async () => { "0x0000000000000000000000000000000000000000000000000000000000000001" ); + let observedName = ""; + let observedInputs: string[] = []; + const oracleCallback: OracleCallback = async ( + name: string, + inputs: string[] + ) => { + // Throwing inside the oracle callback causes a timeout so we log the observed values + // and defer the check against expected values until after the execution is complete. + observedName = name; + observedInputs = inputs; + + // Witness(1) + Witness(2) = 1 + 1 = 2 + return ["0x02"]; + }; const solved_witness: WitnessMap = await executeCircuit( oracle_bytecode, initial_witness, - async (_name: string, _inputs: string[]) => { - // We cannot use jest matchers here (or write to a variable in the outside scope) so cannot test that - // the values for `name` and `inputs` are correct, we can `console.log` them however. - // console.log(name) - // console.log(inputs) - - // Witness(1) + Witness(2) = 1 + 1 = 2 - return ["0x02"]; - } + oracleCallback ); + // Check that expected values were passed to oracle callback. + expect(observedName).toBe("example_oracle"); + expect(observedInputs).toStrictEqual([ + initial_witness.get(1) as string, + initial_witness.get(2) as string, + ]); + // If incorrect value is written into circuit then execution should halt due to unsatisfied constraint in // arithmetic opcode. Nevertheless, check that returned value was inserted correctly. expect(solved_witness.get(3) as string).toBe(