From 13a9ecf375be0b85bdd6cbfc0e93a530b839f5d5 Mon Sep 17 00:00:00 2001 From: James Zaki Date: Wed, 17 Apr 2024 14:05:37 +0100 Subject: [PATCH] Document recursive verification example --- recursive-proofs/README.md | 24 +++++++++---- .../circuits/recurseNode/src/main.nr | 2 +- recursive-proofs/main.ts | 34 ++++++++++++++----- 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/recursive-proofs/README.md b/recursive-proofs/README.md index eac2f771e24..c29cd239c26 100644 --- a/recursive-proofs/README.md +++ b/recursive-proofs/README.md @@ -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` diff --git a/recursive-proofs/circuits/recurseNode/src/main.nr b/recursive-proofs/circuits/recurseNode/src/main.nr index 4a85918e1a8..792c3285ea7 100644 --- a/recursive-proofs/circuits/recurseNode/src/main.nr +++ b/recursive-proofs/circuits/recurseNode/src/main.nr @@ -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( diff --git a/recursive-proofs/main.ts b/recursive-proofs/main.ts index c697457db16..1a64ae114b5 100644 --- a/recursive-proofs/main.ts +++ b/recursive-proofs/main.ts @@ -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); @@ -21,6 +22,7 @@ async function getCircuit(name: string) { const cores = 12; +// Helper function to create Noir objects async function fullNoirFromCircuit(circuitName: string): Promise { const circuit: CompiledCircuit = await getCircuit(circuitName); const backend: BarretenbergBackend = new BarretenbergBackend(circuit, { threads: cores }); @@ -28,25 +30,39 @@ async function fullNoirFromCircuit(circuitName: string): Promise { 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 @@ -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 @@ -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..."); @@ -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();