From 3c29fda0adbd440da484fcab35f303fcd0ee9714 Mon Sep 17 00:00:00 2001 From: TomAFrench Date: Wed, 25 Oct 2023 17:05:24 +0000 Subject: [PATCH 1/5] feat(noir_js): allow providing foreign call handlers in noirJS --- tooling/noir_js/src/program.ts | 8 ++++---- tooling/noir_js/src/witness_generation.ts | 12 +++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/tooling/noir_js/src/program.ts b/tooling/noir_js/src/program.ts index bf48e15fcad..f09fdb34253 100644 --- a/tooling/noir_js/src/program.ts +++ b/tooling/noir_js/src/program.ts @@ -2,13 +2,13 @@ import { Backend, CompiledCircuit, ProofData } from '@noir-lang/types'; import { generateWitness } from './witness_generation.js'; import initAbi, { abiDecode, InputMap, InputValue } from '@noir-lang/noirc_abi'; -import initACVM, { compressWitness } from '@noir-lang/acvm_js'; +import initACVM, { compressWitness, ForeignCallHandler } from '@noir-lang/acvm_js'; export class Noir { constructor( private circuit: CompiledCircuit, private backend?: Backend, - ) {} + ) { } async init(): Promise { // If these are available, then we are in the @@ -29,9 +29,9 @@ export class Noir { } // Initial inputs to your program - async execute(inputs: InputMap): Promise<{ witness: Uint8Array; returnValue: InputValue }> { + async execute(inputs: InputMap, foreignCallHandler?: ForeignCallHandler): Promise<{ witness: Uint8Array; returnValue: InputValue }> { await this.init(); - const witness = await generateWitness(this.circuit, inputs); + const witness = await generateWitness(this.circuit, inputs, foreignCallHandler); const { return_value: returnValue } = abiDecode(this.circuit.abi, witness); return { witness: compressWitness(witness), returnValue }; } diff --git a/tooling/noir_js/src/witness_generation.ts b/tooling/noir_js/src/witness_generation.ts index f96cddb0eca..982530fb233 100644 --- a/tooling/noir_js/src/witness_generation.ts +++ b/tooling/noir_js/src/witness_generation.ts @@ -1,19 +1,21 @@ import { abiEncode, InputMap } from '@noir-lang/noirc_abi'; import { base64Decode } from './base64_decode.js'; -import { executeCircuit, WitnessMap } from '@noir-lang/acvm_js'; +import { executeCircuit, WitnessMap, ForeignCallHandler, ForeignCallInput } from '@noir-lang/acvm_js'; import { CompiledCircuit } from '@noir-lang/types'; +const defaultForeignCallHandler: ForeignCallHandler = (name: string, args: ForeignCallInput[]) => { + throw Error(`Unexpected oracle during execution: ${name}(${args.join(', ')})`) +} + // Generates the witnesses needed to feed into the chosen proving system -export async function generateWitness(compiledProgram: CompiledCircuit, inputs: InputMap): Promise { +export async function generateWitness(compiledProgram: CompiledCircuit, inputs: InputMap, foreignCallHandler: ForeignCallHandler = defaultForeignCallHandler): Promise { // Throws on ABI encoding error const witnessMap = abiEncode(compiledProgram.abi, inputs); // Execute the circuit to generate the rest of the witnesses and serialize // them into a Uint8Array. try { - const solvedWitness = await executeCircuit(base64Decode(compiledProgram.bytecode), witnessMap, () => { - throw Error('unexpected oracle during execution'); - }); + const solvedWitness = await executeCircuit(base64Decode(compiledProgram.bytecode), witnessMap, foreignCallHandler); return solvedWitness; } catch (err) { throw new Error(`Circuit execution failed: ${err}`); From 489c4fb8bd29fee8443844a7f328e5800b51c251 Mon Sep 17 00:00:00 2001 From: TomAFrench Date: Wed, 25 Oct 2023 17:31:34 +0000 Subject: [PATCH 2/5] chore: linter --- tooling/noir_js/src/program.ts | 7 +++++-- tooling/noir_js/src/witness_generation.ts | 10 +++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tooling/noir_js/src/program.ts b/tooling/noir_js/src/program.ts index f09fdb34253..cfd7a715dfe 100644 --- a/tooling/noir_js/src/program.ts +++ b/tooling/noir_js/src/program.ts @@ -8,7 +8,7 @@ export class Noir { constructor( private circuit: CompiledCircuit, private backend?: Backend, - ) { } + ) {} async init(): Promise { // If these are available, then we are in the @@ -29,7 +29,10 @@ export class Noir { } // Initial inputs to your program - async execute(inputs: InputMap, foreignCallHandler?: ForeignCallHandler): Promise<{ witness: Uint8Array; returnValue: InputValue }> { + async execute( + inputs: InputMap, + foreignCallHandler?: ForeignCallHandler, + ): Promise<{ witness: Uint8Array; returnValue: InputValue }> { await this.init(); const witness = await generateWitness(this.circuit, inputs, foreignCallHandler); const { return_value: returnValue } = abiDecode(this.circuit.abi, witness); diff --git a/tooling/noir_js/src/witness_generation.ts b/tooling/noir_js/src/witness_generation.ts index 982530fb233..e3ddb1a2a21 100644 --- a/tooling/noir_js/src/witness_generation.ts +++ b/tooling/noir_js/src/witness_generation.ts @@ -4,11 +4,15 @@ import { executeCircuit, WitnessMap, ForeignCallHandler, ForeignCallInput } from import { CompiledCircuit } from '@noir-lang/types'; const defaultForeignCallHandler: ForeignCallHandler = (name: string, args: ForeignCallInput[]) => { - throw Error(`Unexpected oracle during execution: ${name}(${args.join(', ')})`) -} + throw Error(`Unexpected oracle during execution: ${name}(${args.join(', ')})`); +}; // Generates the witnesses needed to feed into the chosen proving system -export async function generateWitness(compiledProgram: CompiledCircuit, inputs: InputMap, foreignCallHandler: ForeignCallHandler = defaultForeignCallHandler): Promise { +export async function generateWitness( + compiledProgram: CompiledCircuit, + inputs: InputMap, + foreignCallHandler: ForeignCallHandler = defaultForeignCallHandler, +): Promise { // Throws on ABI encoding error const witnessMap = abiEncode(compiledProgram.abi, inputs); From 71e0564afef8a74834e3101ed02a40c402212d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Sat, 28 Oct 2023 12:07:04 +0100 Subject: [PATCH 3/5] chore(docs): adding doc --- docs/docs/noir_js/reference/01_noirjs.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/docs/noir_js/reference/01_noirjs.md b/docs/docs/noir_js/reference/01_noirjs.md index d9e5a0c6115..16754f1d096 100644 --- a/docs/docs/noir_js/reference/01_noirjs.md +++ b/docs/docs/noir_js/reference/01_noirjs.md @@ -58,10 +58,12 @@ await noirInstance.init(); This async method allows to execute a circuit to get its witness and return value. [`generateFinalProof`](#generatefinalproof) calls it for you, but you can call it directly (i.e. to feed directly to a backend, or to get the return value). +You can optionally provide a foreignCallHandler, to handle functions that should run outside of the prover (i.e. println) + ### Syntax ```js -async execute(inputs) +async execute(inputs, foreignCallHandler) ``` ### Parameters @@ -69,6 +71,7 @@ async execute(inputs) | Parameter | Type | Description | | --------- | ------ | ------------------------------------------------ | | `inputs` | Object | An object containing the inputs to your circuit. | +| `foreignCallHandler` (optional) | Function | A function handling the foreign call from your circuit | ### Returns @@ -81,6 +84,7 @@ async execute(inputs) ```js const { witness, returnValue } = await noir.execute(inputs) +const { witness, returnValue } = await noir.execute(inputs, (event) => console.log(event)) ``` ## `generateFinalProof` From 499bac1bad94a6b84ed25ed5915f4ee21c46b8e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Mon, 30 Oct 2023 16:58:19 +0000 Subject: [PATCH 4/5] Update docs/docs/noir_js/reference/01_noirjs.md Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- docs/docs/noir_js/reference/01_noirjs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/noir_js/reference/01_noirjs.md b/docs/docs/noir_js/reference/01_noirjs.md index 16754f1d096..7051d3c7778 100644 --- a/docs/docs/noir_js/reference/01_noirjs.md +++ b/docs/docs/noir_js/reference/01_noirjs.md @@ -84,7 +84,7 @@ async execute(inputs, foreignCallHandler) ```js const { witness, returnValue } = await noir.execute(inputs) -const { witness, returnValue } = await noir.execute(inputs, (event) => console.log(event)) +const { witness, returnValue } = await noir.execute(inputs, (name, args) => console.log(`Received foreign call ${name} with arguments ${args}`)) ``` ## `generateFinalProof` From 54f852d5c762b804ec1aa530aea7a4b0ee125532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Mon, 30 Oct 2023 16:58:39 +0000 Subject: [PATCH 5/5] Update docs/docs/noir_js/reference/01_noirjs.md Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- docs/docs/noir_js/reference/01_noirjs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/noir_js/reference/01_noirjs.md b/docs/docs/noir_js/reference/01_noirjs.md index 7051d3c7778..0d6d5abbbff 100644 --- a/docs/docs/noir_js/reference/01_noirjs.md +++ b/docs/docs/noir_js/reference/01_noirjs.md @@ -58,7 +58,7 @@ await noirInstance.init(); This async method allows to execute a circuit to get its witness and return value. [`generateFinalProof`](#generatefinalproof) calls it for you, but you can call it directly (i.e. to feed directly to a backend, or to get the return value). -You can optionally provide a foreignCallHandler, to handle functions that should run outside of the prover (i.e. println) +You can optionally provide a foreignCallHandler, to handle functions that should run outside of the prover (e.g. `std::println`) ### Syntax