Skip to content

Commit

Permalink
Document recursive verification example
Browse files Browse the repository at this point in the history
  • Loading branch information
jzaki committed Apr 17, 2024
1 parent 9c3d5af commit 13a9ecf
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 16 deletions.
24 changes: 17 additions & 7 deletions recursive-proofs/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
# Noir recursion
# A minimal example of nested recursion

## Install dependencies
## About

`yarn`
The code in this project shows recursive verification of Noir functions.

## Compile all the things
The primary function is simply addition, and verifies the re-calculation of one path up a binary tree from two leaf nodes.
A more useful application of this design would be proving the hash of data in a merkle tree (leaf nodes), up to the merkle root. Amortizing the cost of proving each hash per nested call.

`yarn compile:all`
## The circuits
The function doing the calculation, in this case addition, is in the sumLib. It is used in both recursive circuits: `sum` and `recursiveLeaf`.

## Run main typescript
## Verification
Results of a call to `sum` are verified in `recursiveLeaf`, which itself also calls `sum` again. The results of the `recursiveLeaf` call are then verified in `recursiveNode`.

`yarn start`
That is:
- `recursiveNode` verifies `recursiveLeaf` artifacts
- `recursiveLeaf` verifies `sum` artifacts

## Using this project
- Install dependencies: `yarn`
- Compile all the things: `yarn compile:all`
- Run main typescript: `yarn start`
2 changes: 1 addition & 1 deletion recursive-proofs/circuits/recurseNode/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ fn main(
verification_key: [Field; 114],
public_inputs: pub [Field; 5],
key_hash: Field,
proof: [Field; 93] //109
proof: [Field; 93] // 109 bytes were supposed to be required to verify recursive proofs (WIP)
) -> pub u64 {
// verify sum was computed correctly
std::verify_proof(
Expand Down
34 changes: 26 additions & 8 deletions recursive-proofs/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { compile, createFileManager } from '@noir-lang/noir_wasm';

import { ProofData } from '@noir-lang/types';

// Helper function to get compiled Noir program
async function getCircuit(name: string) {
const basePath = resolve(join('./circuits', name));
const fm = createFileManager(basePath);
Expand All @@ -21,32 +22,47 @@ async function getCircuit(name: string) {

const cores = 12;

// Helper function to create Noir objects
async function fullNoirFromCircuit(circuitName: string): Promise<FullNoir> {
const circuit: CompiledCircuit = await getCircuit(circuitName);
const backend: BarretenbergBackend = new BarretenbergBackend(circuit, { threads: cores });
const noir: Noir = new Noir(circuit, backend);
return { circuit, backend, noir };
}

// Type to associate related Noir objects
type FullNoir = {
circuit: CompiledCircuit,
backend: BarretenbergBackend,
noir: Noir
}

// Calculate example sum of two leaf nodes up left branch
// S3
// S2 9
// / \
// / \
// S1 4 5
// / \ / \
// 1 3 # #


async function start() {
const leaf: FullNoir = await fullNoirFromCircuit('sum');
const recurseLeaf: FullNoir = await fullNoirFromCircuit('recurseLeaf');
const recurseNode: FullNoir = await fullNoirFromCircuit('recurseNode');
// Create Noir objects for each circuit
const leaf: FullNoir = await fullNoirFromCircuit('sum'); // a + b = c
const recurseLeaf: FullNoir = await fullNoirFromCircuit('recurseLeaf'); // verify l1 + l2 = n1, then sum n1 + n2
const recurseNode: FullNoir = await fullNoirFromCircuit('recurseNode'); // verify n1 + n2 = root1

// Generate leaf proof artifacts (S1, addition of 1 and 3)

// Leaf params of left branch
const leafParams = { a: 1, b: 3 };
let numPubInputs = 2;

// Generate leaf proof artifacts
let { witness, returnValue } = await leaf.noir.execute(leafParams);
console.log("leaf: %d + %d = ", ...Object.values(leafParams), Number(returnValue).toString());
const innerProof1: ProofData = await leaf.backend.generateProof(witness);
console.log("Generating intermediate proof artifacts leaf...");
console.log("Generating intermediate proof artifacts for leaf calculation...");
const artifacts1 = await leaf.backend.generateRecursiveProofArtifacts(
innerProof1,
numPubInputs + 1 // +1 for public return
Expand All @@ -58,8 +74,9 @@ async function start() {
];

const a = returnValue;
const b = 5;
const b = 5; // Sum of leaf branches beneath right node

// Generate node proof artifacts (S2: verify 1+3=4 proof, add 5)
const nodeParams = {
verification_key: artifacts1.vkAsFields,
public_inputs: pub_inputs, // public, each counted individually
Expand All @@ -82,12 +99,12 @@ async function start() {
pub_inputs.push(returnValue.toString()); // leaf returns sum
pub_inputs.push(returnValue.toString()); // node also coded to return same value

// Generate and verify outer proof
// Generate outer proof artifacts (S3: verify 4+5=9)
const outerParams = {
verification_key: artifacts2.vkAsFields,
public_inputs: pub_inputs,
key_hash: artifacts2.vkHash,
proof: artifacts2.proofAsFields
proof: artifacts2.proofAsFields // the proof size of a function that verifies another proof was expected to be 109 bytes, but was still 93
};

console.log("Executing...");
Expand All @@ -98,6 +115,7 @@ async function start() {
const resNode: boolean = await recurseNode.backend.verifyProof(outerProof);
console.log("Verification", resNode ? "PASSED" : "failed");

// Cleanup
recurseNode.backend.destroy();
recurseLeaf.backend.destroy();
leaf.backend.destroy();
Expand Down

0 comments on commit 13a9ecf

Please sign in to comment.