Skip to content

Commit

Permalink
wrapping up noir js integration
Browse files Browse the repository at this point in the history
  • Loading branch information
signorecello committed Oct 5, 2023
1 parent 71263eb commit 9075935
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 223 deletions.
57 changes: 31 additions & 26 deletions next-hardhat/components/component.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, SetStateAction } from 'react';

import { toast } from 'react-toastify';
import Ethers from '../utils/ethers';
import React from 'react';
import { Noir } from '@kevaundray/noir_js';
import { BarretenbergBackend } from '@kevaundray/backend_barretenberg';

import { Noir } from '@noir-lang/noir_js';
import { BarretenbergBackend } from '@noir-lang/backend_barretenberg';
import circuit from '../circuits/target/noirstarter.json';

function Component() {
const [input, setInput] = useState({ x: 0, y: 0 });
const [proof, setProof] = useState(Uint8Array.from([]));
const [backend, setBackend] = useState(new BarretenbergBackend(circuit));
const [noir, setNoir] = useState(new Noir(circuit, backend));
const [noir, setNoir] = useState<Noir | null>(null);
const [backend, setBackend] = useState<BarretenbergBackend | null>(null);

// Handles input state
const handleChange = e => {
Expand All @@ -21,8 +23,8 @@ function Component() {
// Calculates proof
const calculateProof = async () => {
const calc = new Promise(async (resolve, reject) => {
const proof = await noir.generateFinalProof(input);
console.log("Proof created: ", proof);
const proof = await noir!.generateFinalProof(input);
console.log('Proof created: ', proof);
setProof(proof);
resolve(proof);
});
Expand All @@ -36,21 +38,26 @@ function Component() {
const verifyProof = async () => {
const verifyOffChain = new Promise(async (resolve, reject) => {
if (proof) {
const verification = await noir.verifyFinalProof(proof);
console.log("Proof verified: ", verification);
const verification = await noir!.verifyFinalProof(proof);
console.log('Proof verified: ', verification);
resolve(verification);
}
});

const verifyOnChain = new Promise(async (resolve, reject) => {
if (proof) {
if (!proof) return reject(new Error('No proof'));
if (!window.ethereum) return reject(new Error('No ethereum provider'));
try {
const ethers = new Ethers();

const publicInputs = proof.slice(0, 32);
const slicedProof = proof.slice(32);

const verification = await ethers.contract.verify(slicedProof, [publicInputs]);
resolve(verification);
} catch (err) {
console.log(err);
reject(new Error("Couldn't verify proof on-chain"));
}
});

Expand All @@ -63,7 +70,11 @@ function Component() {
toast.promise(verifyOnChain, {
pending: 'Verifying proof on-chain...',
success: 'Proof verified on-chain!',
error: 'Error verifying proof on-chain',
error: {
render({ data }: any) {
return `Error: ${data.message}`;
},
},
});
};

Expand All @@ -73,37 +84,31 @@ function Component() {
verifyProof();

return () => {
// TODO: we can add a destroy method to Noir to save on memory
// noir.destroy();
// TODO: Backend should be destroyed by Noir JS so we don't have to
// store backend in state
backend!.destroy();
};
}
}, [proof]);

const initNoir = async () => {
const init = new Promise(async (resolve, reject) => {
await noir.init();

// Init of the backend is done dynamically when we call generateFinalProof
// Or any method that requires the circuit to be initialized
setBackend(backend);
resolve(backend);
setNoir(noir);
resolve(noir);
});

toast.promise(init, {
const backend = new BarretenbergBackend(circuit);
setBackend(backend);
const noir = new Noir(circuit, backend);
await toast.promise(noir.init(), {
pending: 'Initializing Noir...',
success: 'Noir initialized!',
error: 'Error initializing Noir',
});
setNoir(noir);
};

useEffect(() => {
initNoir();
}, []);

return (
<div className="gameContainer">
<div className="container">
<h1>Example starter</h1>
<h2>This circuit checks that x and y are different</h2>
<p>Try it!</p>
Expand Down
9 changes: 4 additions & 5 deletions next-hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
},
"packageManager": "yarn@3.6.3",
"dependencies": {
"@kevaundray/backend_barretenberg": "0.7.10-kw-play-around-f3626e5f8",
"@kevaundray/noir_js": "0.15.0-kw-play-around-f3626e5f8",
"@noir-lang/backend_barretenberg": "^0.7.10",
"@noir-lang/noir_js": "^0.16.0",
"@nomicfoundation/hardhat-chai-matchers": "^1.0.6",
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^2.0.1",
Expand All @@ -21,21 +21,20 @@
"@typechain/ethers-v5": "^10.1.0",
"@typechain/hardhat": "^6.1.2",
"dotenv": "^16.0.3",
"ethers": "^5.7.2",
"hardhat": "^2.17.0",
"hardhat-gas-reporter": "^1.0.8",
"next": "latest",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-toastify": "^9.1.1",
"solidity-coverage": "^0.8.1",
"typechain": "^8.1.0"
},
"devDependencies": {
"@types/mocha": "^10.0.1",
"@types/node": "^18.15.5",
"@types/react": "^18.0.26",
"chai": "^4.2.0",
"hardhat-gas-reporter": "^1.0.9",
"solidity-coverage": "^0.8.5",
"ts-node": "^10.9.1",
"typescript": "^4.9.3"
}
Expand Down
38 changes: 14 additions & 24 deletions next-hardhat/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,40 @@ import { expect } from 'chai';
import hre from 'hardhat';

import circuit from '../circuits/target/noirstarter.json';
import { Noir } from '@kevaundray/noir_js';
import { BarretenbergBackend } from '@kevaundray/backend_barretenberg';
import { Noir } from '@noir-lang/noir_js';
import { BarretenbergBackend } from '@noir-lang/backend_barretenberg';

describe('It compiles noir program code, receiving circuit bytes and abi object.', () => {
let noir: Noir;
let correctProof: Uint8Array;

before(async () => {
const verifierContract = await hre.ethers.deployContract('UltraVerifier');

const verifierAddr = await verifierContract.deployed();
console.log(`Verifier deployed to ${verifierAddr.address}`);

const backend = new BarretenbergBackend(circuit);
noir = new Noir(circuit, backend);
});

it('Should generate valid proof for correct input', async () => {
const input = { x: 1, y: 2 };

const backend = new BarretenbergBackend(circuit);
// Initialize program
const program = new Noir(circuit, backend);
// Generate proof
const proof = await program.generateFinalProof(input);

// Proof verification
const isValid = await program.verifyFinalProof(proof);
expect(isValid).to.be.true;
correctProof = await noir.generateFinalProof(input);
expect(correctProof instanceof Uint8Array).to.be.true;
});

// TODO(Ze): My understanding is that this test was previously using a global noir instance
// TODO: Why was this the case and is this more indicative of the real workflow?
// it('Should verify valid proof for correct input', async () => {
// const verification = await program.verifyFinalProof(correctProof);
// expect(verification).to.be.true;
// });
it('Should verify valid proof for correct input', async () => {
const verification = await noir.verifyFinalProof(correctProof);
expect(verification).to.be.true;
});

it('Should fail to generate valid proof for incorrect input', async () => {
try {
const input = { x: 1, y: 1 };

const backend = new BarretenbergBackend(circuit);
// Initialize program
const program = new Noir(circuit, backend);
// Generate proof
await program.generateFinalProof(input);
const incorrectProof = await noir.generateFinalProof(input);
} catch (err) {

// TODO(Ze): Not sure how detailed we want this test to be
expect(err instanceof Error).to.be.true;
const error = err as Error;
Expand Down
Loading

0 comments on commit 9075935

Please sign in to comment.