diff --git a/docs/docs/noir_js/reference/01_noirjs.md b/docs/docs/noir_js/reference/01_noirjs.md index d9e5a0c6115..0d6d5abbbff 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 (e.g. `std::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, (name, args) => console.log(`Received foreign call ${name} with arguments ${args}`)) ``` ## `generateFinalProof` diff --git a/tooling/noir_js/src/program.ts b/tooling/noir_js/src/program.ts index bf48e15fcad..cfd7a715dfe 100644 --- a/tooling/noir_js/src/program.ts +++ b/tooling/noir_js/src/program.ts @@ -2,7 +2,7 @@ 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( @@ -29,9 +29,12 @@ 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..e3ddb1a2a21 100644 --- a/tooling/noir_js/src/witness_generation.ts +++ b/tooling/noir_js/src/witness_generation.ts @@ -1,19 +1,25 @@ 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}`);