Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: update recursion example for noir v0.25.0 #18

Merged
merged 7 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions recursion/.prettierrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"arrowParens": "avoid",
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"proseWrap": "always"
"arrowParens": "avoid",
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"proseWrap": "always"
}
Binary file modified recursion/.yarn/install-state.gz
Binary file not shown.
12 changes: 8 additions & 4 deletions recursion/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
# Recursive proofs with Noir

Recursive proofs mean you prove that another proof is correct. A bit of a proofinception but bear with me:
Recursive proofs mean you prove that another proof is correct. A bit of a proofinception but bear
with me:

- You prove that `x != y`
- You pick that proof and send it to another circuit (the "outer" proof)
- You generate the outer proof and verify it

Why is this useful? In this example, it doesn't do much. But you could verify two proofs within a proof, which can be incredibly useful.
Why is this useful? In this example, it doesn't do much. But you could verify two proofs within a
proof, which can be incredibly useful.

You could also avoid verifying stuff on-chain for turn-based games, for example. Check out the [Noir Docs](https://noir-lang.org/docs/explainers/explainer-recursion) for a high-level explanation.
You could also avoid verifying stuff on-chain for turn-based games, for example. Check out the
[Noir Docs](https://noir-lang.org/docs/explainers/explainer-recursion) for a high-level explanation.

## Getting Started

1. Install dependencies by running `yarn`
2. For on-chain verification, open another terminal, and run `cd packages/hardhat && npx hardhat node`
2. For on-chain verification, open another terminal, and run
`cd packages/hardhat && npx hardhat node`
3. Run `yarn dev`

## Testing
Expand Down
42 changes: 21 additions & 21 deletions recursion/package.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
{
"name": "recursion",
"private": true,
"packageManager": "[email protected]",
"workspaces": [
"packages/hardhat",
"packages/noir",
"packages/next"
],
"engines": {
"node": "^20.10.0"
},
"scripts": {
"dev": "yarn workspace hardhat run deploy && yarn workspace next run dev",
"test": "yarn workspace hardhat run test",
"build": "yarn workspace next run build"
},
"dependencies": {
"@noir-lang/backend_barretenberg": "^0.23.0",
"@noir-lang/noir_js": "^0.23.0",
"@noir-lang/noir_wasm": "^0.23.0"
}
"name": "recursion",
"private": true,
"packageManager": "[email protected]",
"workspaces": [
"packages/hardhat",
"packages/noir",
"packages/next"
],
"engines": {
"node": "^20.10.0"
},
"scripts": {
"dev": "yarn workspace hardhat run deploy && yarn workspace next run dev",
"test": "yarn workspace hardhat run test",
"build": "yarn workspace next run build"
},
"dependencies": {
"@noir-lang/backend_barretenberg": "^0.25.0",
"@noir-lang/noir_js": "^0.25.0",
"@noir-lang/noir_wasm": "^0.25.0"
}
}
2 changes: 1 addition & 1 deletion recursion/packages/addresses.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"chainId":31337,"verifier":"0xdc64a140aa3e981100a9beca4e685f962f0cf6c9"}
{"chainId":31337,"verifier":"0xe7f1725e7734ce288f8367e1bb143e90bb3f0512"}
10 changes: 5 additions & 5 deletions recursion/packages/hardhat/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,17 @@ describe('It compiles noir program code, receiving circuit bytes and abi object.
describe.only('Proof generation', async () => {
it('Should generate an intermediate proof', async () => {
const { witness } = await noirs.main.execute(mainInput);
intermediateProof = await backends.main.generateIntermediateProof(witness);
intermediateProof = await backends.main.generateProof(witness);

const { proof, publicInputs } = intermediateProof;
expect(proof instanceof Uint8Array).to.be.true;

const verified = await backends.main.verifyIntermediateProof({ proof, publicInputs });
const verified = await backends.main.verifyProof({ proof, publicInputs });
expect(verified).to.be.true;

const numPublicInputs = 1;
const { proofAsFields, vkAsFields, vkHash } =
await backends.main.generateIntermediateProofArtifacts(
await backends.main.generateRecursiveProofArtifacts(
{ publicInputs, proof },
numPublicInputs,
);
Expand All @@ -81,7 +81,7 @@ describe('It compiles noir program code, receiving circuit bytes and abi object.
});

it('Should generate a final proof with a recursive input', async () => {
finalProof = await noirs.recursive.generateFinalProof(recursiveInputs);
finalProof = await noirs.recursive.generateProof(recursiveInputs);
expect(finalProof.proof instanceof Uint8Array).to.be.true;
});
});
Expand All @@ -94,7 +94,7 @@ describe('It compiles noir program code, receiving circuit bytes and abi object.
});

it('Should verify off-chain', async () => {
const verified = await noirs.recursive.verifyFinalProof(finalProof);
const verified = await noirs.recursive.verifyProof(finalProof);
expect(verified).to.be.true;
});

Expand Down
21 changes: 10 additions & 11 deletions recursion/packages/next/hooks/useMainProofGeneration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,29 @@ export function useMainProofGeneration(inputs?: { x: string; y: string }) {
const [mainProofArtifacts, setMainProofArtifacts] = useState<ProofArtifacts>();

const proofGeneration = async () => {
if (!inputs) return;
if (!inputs) {
return;
}

const circuit = await getCircuit('main');
const backend = new BarretenbergBackend(circuit, { threads: navigator.hardwareConcurrency });
const noir = new Noir(circuit, backend);

const { witness } = await noir.execute(inputs);

const { publicInputs, proof } = await toast.promise(
backend.generateIntermediateProof(witness),
{
pending: 'Generating proof',
success: 'Proof generated',
error: 'Error generating proof',
},
);
const { publicInputs, proof } = await toast.promise(backend.generateProof(witness), {
pending: 'Generating proof',
success: 'Proof generated',
error: 'Error generating proof',
});

toast.promise(backend.verifyIntermediateProof({ proof, publicInputs }), {
toast.promise(backend.verifyProof({ proof, publicInputs }), {
pending: 'Verifying intermediate proof',
success: 'Intermediate proof verified',
error: 'Error verifying intermediate proof',
});

const mainProofArtifacts = await backend.generateIntermediateProofArtifacts(
const mainProofArtifacts = await backend.generateRecursiveProofArtifacts(
{ publicInputs, proof },
1, // 1 public input
);
Expand Down
13 changes: 11 additions & 2 deletions recursion/packages/next/hooks/useOffChainVerification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,26 @@ import { useEffect } from 'react';
import { toast } from 'react-toastify';
import { BarretenbergBackend } from '@noir-lang/backend_barretenberg';

export function useOffChainVerification(backend?: BarretenbergBackend, proofData?: ProofData) {
export function useOffChainVerification(
backend?: BarretenbergBackend,
proofData?: ProofData,
stopLoading?: () => void,
) {
useEffect(() => {
if (!proofData || !backend) return;
const { proof, publicInputs } = proofData;

toast.promise(backend.verifyFinalProof({ proof, publicInputs }), {
const verificationPromise = backend.verifyProof({ proof, publicInputs });
toast.promise(verificationPromise, {
pending: 'Verifying recursive proof off-chain',
success: 'Recursive proof verified off-chain',
error: 'Error verifying recursive proof off-chain',
});

verificationPromise.then(() => {
stopLoading?.();
});

// return () => {
// backend.destroy();
// };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function useRecursiveProofGeneration(
};

const { witness } = await noir.execute(recInput);
const proofData = await backend.generateFinalProof(witness);
const proofData = await backend.generateProof(witness);

setRecursiveBackend(backend);
setProofData(proofData);
Expand Down
2 changes: 1 addition & 1 deletion recursion/packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"typescript": "5.0.4"
},
"dependencies": {
"@noir-lang/types": "^0.23.0",
"@noir-lang/types": "^0.25.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-toastify": "^10.0.4",
Expand Down
70 changes: 70 additions & 0 deletions recursion/packages/next/pages/Page.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* Page.module.css */
.container {
max-width: 600px;
margin: 2rem auto;
padding: 2rem;
background: #f9f9f9;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.title {
color: #333;
text-align: center;
}

.proofForm {
display: flex;
flex-direction: column;
gap: 1rem;
}

.inputGroup {
display: flex;
flex-direction: column;
gap: 0.5rem;
}

.inputLabel {
font-size: 1rem;
color: #666;
}

.inputField {
padding: 0.5rem;
font-size: 1rem;
border: 1px solid #ddd;
border-radius: 4px;
}

.submitButton {
padding: 0.5rem 1rem;
font-size: 1rem;
color: white;
background-color: #5c6bc0;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}

.submitButton:hover {
background-color: #3f51b5;
}

.submitButton:disabled {
background-color: #9fa8da;
cursor: not-allowed;
}

.errorMessage {
color: #b71c1c;
text-align: center;
margin-top: 1rem;
}

.successMessage {
color: #2e7d32;
text-align: center;
margin-top: 1rem;
}
62 changes: 51 additions & 11 deletions recursion/packages/next/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
'use client';

import React, { useState } from 'react';
import styles from './Page.module.css';

import React, { useState, useCallback } from 'react';
import { useOffChainVerification } from '../hooks/useOffChainVerification';
import { useOnChainVerification } from '../hooks/useOnChainVerification';
import { useMainProofGeneration } from '../hooks/useMainProofGeneration';
import { useRecursiveProofGeneration } from '../hooks/useRecursiveProofGeneration';

export default function Page() {
const [xValue, setXValue] = useState('');
const [yValue, setYValue] = useState('');
const [loading, setLoading] = useState(false);
const stopLoading = useCallback(() => {
setLoading(false);
}, []);

const [input, setInput] = useState<{ x: string; y: string } | undefined>();
const mainProofArtifacts = useMainProofGeneration(input);
const { recursiveBackend, proofData } = useRecursiveProofGeneration(mainProofArtifacts, input);

useOffChainVerification(recursiveBackend, proofData);
useOffChainVerification(recursiveBackend, proofData, stopLoading);
useOnChainVerification(proofData);

const submit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setLoading(true);
const elements = e.currentTarget.elements;
if (!elements) return;

Expand All @@ -25,18 +35,48 @@ export default function Page() {
setInput({ x: x.value, y: y.value });
};

const inputsFilled = xValue.trim() !== '' && yValue.trim() !== '';
const isDisabled = loading || !inputsFilled;

return (
<form className="gameContainer" onSubmit={submit}>
<h1>Recursive!</h1>
<p>This circuit proves that x and y are different (main proof)</p>
<div className={styles.container}>
<h1 className={styles.title}>Recursive!</h1>
<p>This circuit proves that x and y are different (main proof).</p>
<p>
Then it feeds that proof into another circuit, which then proves it verifies (recursive
proof)
proof).
</p>
<h2>Try it!</h2>
<input name="x" type={'text'} />
<input name="y" type={'text'} />
<button type="submit">Calculate proof</button>
</form>
<form className={styles.proofForm} onSubmit={submit}>
<div className={styles.inputGroup}>
<label htmlFor="x-input" className={styles.inputLabel}>
Value of x:
</label>
<input
id="x-input"
name="x"
type="text"
className={styles.inputField}
value={xValue}
onChange={e => setXValue(e.target.value)}
/>
</div>
<div className={styles.inputGroup}>
<label htmlFor="y-input" className={styles.inputLabel}>
Value of y:
</label>
<input
id="y-input"
name="y"
type="text"
className={styles.inputField}
value={yValue}
onChange={e => setYValue(e.target.value)}
/>
</div>
<button type="submit" className={styles.submitButton} disabled={isDisabled}>
Calculate proof
</button>
</form>
</div>
);
}
Loading