Skip to content

Commit 6ef908c

Browse files
committed
Add initial Next.js project structure for Noir UH Starter
- Created essential files including package.json, TypeScript configuration, and Playwright configuration. - Added global styles and layout components for the application. - Implemented a proof generation component with state management and proof verification logic. - Included a README for setup and usage instructions. - Established a .gitignore to exclude unnecessary files and directories.
1 parent 10a41bf commit 6ef908c

File tree

11 files changed

+242
-0
lines changed

11 files changed

+242
-0
lines changed

web-starter/web/next/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules/
2+
.next/
3+
dist/
4+
.env*
5+
yarn.lock
6+
npm-debug.log*
7+
.DS_Store

web-starter/web/next/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Introduction
2+
3+
A simple Noir circuit with browser proving with bb.js
4+
This is a Next.js version, similar to the vite and webpack examples.
5+
6+
Tested with Noir 1.0.0-beta.6, bb 0.84.0, and Next.js 14.
7+
8+
## Setup
9+
10+
```bash
11+
(cd ../../circuits && ./build.sh)
12+
yarn
13+
```
14+
15+
## Run
16+
17+
```bash
18+
yarn dev
19+
```
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
body {
2+
font-family: Arial, sans-serif;
3+
background: #f8f8f8;
4+
margin: 0;
5+
padding: 0;
6+
}
7+
8+
.container, main {
9+
max-width: 600px;
10+
margin: 40px auto;
11+
background: #fff;
12+
border-radius: 8px;
13+
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
14+
padding: 32px;
15+
}
16+
17+
h1 {
18+
font-size: 2rem;
19+
margin-bottom: 1.5rem;
20+
}
21+
22+
button {
23+
padding: 0.5rem 1.5rem;
24+
font-size: 1rem;
25+
border-radius: 4px;
26+
border: none;
27+
background: #0070f3;
28+
color: #fff;
29+
cursor: pointer;
30+
margin-bottom: 1rem;
31+
}
32+
33+
button:disabled {
34+
background: #aaa;
35+
cursor: not-allowed;
36+
}
37+
38+
#result {
39+
background: #f4f4f4;
40+
border-radius: 4px;
41+
padding: 1rem;
42+
min-height: 2rem;
43+
margin-top: 1rem;
44+
font-family: monospace;
45+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import './globals.css';
2+
import type { ReactNode } from 'react';
3+
4+
export default function RootLayout({ children }: { children: ReactNode }) {
5+
return (
6+
<html lang="en">
7+
<body>
8+
{children}
9+
</body>
10+
</html>
11+
);
12+
}

web-starter/web/next/app/page.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import ProofComponent from './proof-component';
2+
3+
export default function HomePage() {
4+
return (
5+
<main className="container">
6+
<h1>Noir UH Starter (Next.js)</h1>
7+
<ProofComponent />
8+
</main>
9+
);
10+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use client';
2+
import { useState } from 'react';
3+
import { UltraHonkBackend } from '@aztec/bb.js';
4+
import circuit from '../../../circuits/target/noir_uh_starter.json';
5+
import { Noir } from '@noir-lang/noir_js';
6+
7+
export default function ProofComponent() {
8+
const [result, setResult] = useState('');
9+
const [loading, setLoading] = useState(false);
10+
11+
async function generateProof() {
12+
setLoading(true);
13+
setResult(prev => prev + 'Generating proof...\n\n');
14+
try {
15+
const noir = new Noir(circuit as any);
16+
const honk = new UltraHonkBackend((circuit as any).bytecode, { threads: 8 });
17+
const inputs = { x: 3, y: 3 };
18+
const { witness } = await noir.execute(inputs);
19+
const { proof, publicInputs } = await honk.generateProof(witness);
20+
setResult(prev => prev + 'Proof: ' + proof + '\n\n');
21+
setResult(prev => prev + 'Public inputs: ' + publicInputs + '\n\n');
22+
const verified = await honk.verifyProof({ proof, publicInputs });
23+
setResult(prev => prev + 'Verified: ' + verified + '\n\n');
24+
} catch (error) {
25+
setResult(prev => prev + 'Error: ' + error + '\n\n');
26+
}
27+
setLoading(false);
28+
}
29+
30+
return (
31+
<div>
32+
<button id="generateProofBtn" onClick={generateProof} disabled={loading}>
33+
{loading ? 'Generating...' : 'Generate Proof'}
34+
</button>
35+
<div style={{ whiteSpace: 'pre-wrap' }} id="result">{result}</div>
36+
</div>
37+
);
38+
}

web-starter/web/next/next-env.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/image-types/global" />
3+
4+
// NOTE: This file should not be edited
5+
// see https://nextjs.org/docs/basic-features/typescript for more information.

web-starter/web/next/package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "nextjs-noir",
3+
"type": "module",
4+
"dependencies": {
5+
"next": "14.2.3",
6+
"react": "18.2.0",
7+
"react-dom": "18.2.0",
8+
"@aztec/bb.js": "0.84.0",
9+
"@noir-lang/noir_js": "1.0.0-beta.6"
10+
},
11+
"devDependencies": {
12+
"@playwright/test": "^1.53.0",
13+
"@types/node": "^22.10.1",
14+
"@types/react": "^18.2.62",
15+
"@types/react-dom": "^18.2.19",
16+
"typescript": "^5.8.3"
17+
},
18+
"scripts": {
19+
"dev": "next dev",
20+
"build": "next build",
21+
"start": "next start",
22+
"test:e2e": "playwright test"
23+
},
24+
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
25+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// playwright.config.js
2+
// @ts-check
3+
/** @type {import('@playwright/test').PlaywrightTestConfig} */
4+
const config = {
5+
webServer: {
6+
command: 'yarn dev',
7+
port: 3001,
8+
timeout: 120 * 1000,
9+
reuseExistingServer: !process.env.CI,
10+
},
11+
use: {
12+
baseURL: 'http://localhost:3001',
13+
headless: true,
14+
},
15+
projects: [
16+
{ name: 'chromium', use: { browserName: 'chromium' } },
17+
{ name: 'firefox', use: { browserName: 'firefox' } },
18+
{ name: 'webkit', use: { browserName: 'webkit' } },
19+
],
20+
};
21+
22+
module.exports = config;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { test, expect, Page } from '@playwright/test';
2+
3+
// Next.js version
4+
test('proof verification works in the browser', async ({ page }: { page: Page }) => {
5+
await page.goto('/');
6+
await page.click('#generateProofBtn');
7+
// Wait for the result to contain 'Verified:' with increased timeout
8+
let resultText = '';
9+
try {
10+
await expect(page.locator('#result')).toContainText('Verified:', { timeout: 30000 });
11+
resultText = await page.locator('#result').innerText();
12+
} catch (e) {
13+
// Debug: print the current contents of #result if the check fails
14+
resultText = await page.locator('#result').innerText();
15+
console.log('DEBUG: #result contents at failure:', resultText);
16+
throw e;
17+
}
18+
// Check that the result contains 'Verified: true' (or similar)
19+
expect(resultText).toMatch(/Verified:\s*(true|1)/i);
20+
});

0 commit comments

Comments
 (0)