Skip to content

Commit

Permalink
feat(noir_js): allow providing foreign call handlers in noirJS (#3294)
Browse files Browse the repository at this point in the history
Co-authored-by: kevaundray <[email protected]>
Co-authored-by: José Pedro Sousa <[email protected]>
Co-authored-by: José Pedro Sousa <[email protected]>
  • Loading branch information
4 people authored Oct 30, 2023
1 parent 0f9af65 commit c76b0f8
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 9 deletions.
6 changes: 5 additions & 1 deletion docs/docs/noir_js/reference/01_noirjs.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,20 @@ 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

| 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

Expand All @@ -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`
Expand Down
9 changes: 6 additions & 3 deletions tooling/noir_js/src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 };
}
Expand Down
16 changes: 11 additions & 5 deletions tooling/noir_js/src/witness_generation.ts
Original file line number Diff line number Diff line change
@@ -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<WitnessMap> {
export async function generateWitness(
compiledProgram: CompiledCircuit,
inputs: InputMap,
foreignCallHandler: ForeignCallHandler = defaultForeignCallHandler,
): Promise<WitnessMap> {
// 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}`);
Expand Down

0 comments on commit c76b0f8

Please sign in to comment.