diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..963354f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "printWidth": 120 +} diff --git a/Nargo.toml b/Nargo.toml index 588241f..f594662 100644 --- a/Nargo.toml +++ b/Nargo.toml @@ -1,8 +1,9 @@ [workspace] members = [ "circuits/poseidon", + "circuits/rlp", "circuits/keccak", "circuits/keccak_2x", - "circuits/rlp" + "circuits/recursive_poseidon" ] default-member = "circuits/keccak" \ No newline at end of file diff --git a/circuits/recursive_poseidon/Nargo.toml b/circuits/recursive_poseidon/Nargo.toml new file mode 100644 index 0000000..95a4320 --- /dev/null +++ b/circuits/recursive_poseidon/Nargo.toml @@ -0,0 +1,3 @@ +[package] +name = "recursive_poseidon" +type = "bin" diff --git a/circuits/recursive_poseidon/src/main.nr b/circuits/recursive_poseidon/src/main.nr new file mode 100644 index 0000000..6dce514 --- /dev/null +++ b/circuits/recursive_poseidon/src/main.nr @@ -0,0 +1,15 @@ +use dep::std; + +fn main( + verification_key: [Field; 114], + proof: [Field; 93], + public_inputs: [Field; 1], + key_hash: Field +) { + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash + ); +} diff --git a/package.json b/package.json index 606f335..139bafa 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "yargs": "^17.7.2" }, "scripts": { + "temp": "tsx src/temp.ts", "build": "tsc", "start": "node dist/index.js" }, diff --git a/src/index.ts b/src/index.ts index f1706ee..b455dd6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,28 +1,12 @@ -import { - readInputMap, - readProof, - readWitnessMap, - readCircuit, -} from "./utils.js"; +import { readInputMap, readProof, readWitnessMap, initCircuit } from "./utils.js"; import assert from "assert"; -import os from "os"; -import { BarretenbergBackend } from "@noir-lang/backend_barretenberg"; -import { Noir } from "@noir-lang/noir_js"; import { Action, argv } from "./cli.js"; -const PROOF_PATH = `./proofs/${argv.package}.proof`; -const VERIFIER_MAP_PATH = `./circuits/${argv.package}/Verifier.toml`; -const PROVER_MAP_PATH = `./circuits/${argv.package}/Prover.toml`; - -const circuit = await readCircuit(argv.package); -const backend = new BarretenbergBackend(circuit, { - threads: os.cpus().length, -}); -const noir = new Noir(circuit, backend); +const { noir, circuit } = await initCircuit(argv.package); if (argv.action == Action.Verify) { - let proof = await readProof(PROOF_PATH); - let witnessMap = await readWitnessMap(VERIFIER_MAP_PATH, circuit.abi); + let proof = await readProof(argv.package); + let witnessMap = await readWitnessMap(argv.package, circuit.abi); const proofData = { proof, publicInputs: Array.from(witnessMap.values()), @@ -30,7 +14,7 @@ if (argv.action == Action.Verify) { let isCorrect = await noir.verifyFinalProof(proofData); assert(isCorrect, "Proof vefification failed"); } else if (argv.action == Action.Prove) { - let inputMap = await readInputMap(PROVER_MAP_PATH); + let inputMap = await readInputMap(argv.package); await noir.generateFinalProof(inputMap); } diff --git a/src/temp.ts b/src/temp.ts new file mode 100644 index 0000000..c8f9334 --- /dev/null +++ b/src/temp.ts @@ -0,0 +1,51 @@ +import { initCircuit, readInputMap } from "./utils.js"; +import { InputMap } from "@noir-lang/noir_js"; +import { Field } from "@noir-lang/noirc_abi"; +import assert from "assert"; + +const packageName = "poseidon"; +const poseidon = await initCircuit(packageName); + +const poseidonInputs = await readInputMap(packageName); + +let { witness: poseidonWitness } = await poseidon.noir.execute(poseidonInputs); + +console.time("poseidon.bb.generateIntermediateProof"); +const poseidonProof = await poseidon.bb.generateIntermediateProof(poseidonWitness); +console.timeEnd("poseidon.bb.generateIntermediateProof"); + +console.time("poseidon.bb.verifyIntermediateProof"); +const poseidonProofVerification = await poseidon.bb.verifyIntermediateProof(poseidonProof); +assert(poseidonProofVerification, "Poseidon proof verification failed"); +console.timeEnd("poseidon.bb.verifyIntermediateProof"); + +console.time("generateIntermediateProofArtifacts"); +const numPublicInputs = 1; +const { proofAsFields, vkAsFields, vkHash } = await poseidon.bb.generateIntermediateProofArtifacts( + poseidonProof, + numPublicInputs, +); +console.timeEnd("generateIntermediateProofArtifacts"); + +await poseidon.noir.destroy(); + +// RECURSIVE PROOF +const recursivePoseidon = await initCircuit("recursive_poseidon"); +const recursionInputs: InputMap = { + verification_key: vkAsFields, + proof: proofAsFields, + public_inputs: [(await readInputMap(packageName)).x as Field], + key_hash: vkHash, +}; +const { witness: recursivePoseidonWitness } = await recursivePoseidon.noir.execute(recursionInputs); + +console.time("recursivePoseidon.bb.generateFinalProof"); +const recursivePoseidonProof = await recursivePoseidon.bb.generateFinalProof(recursivePoseidonWitness); +console.timeEnd("recursivePoseidon.bb.generateFinalProof"); + +console.time("recursivePoseidon.bb.verifyFinalProof"); +const recursivePoseidonProofVerification = await recursivePoseidon.bb.verifyFinalProof(recursivePoseidonProof); +assert(recursivePoseidonProofVerification, "Recursive Poseidon proof verification failed"); +console.timeEnd("recursivePoseidon.bb.verifyFinalProof"); + +await recursivePoseidon.noir.destroy(); diff --git a/src/utils.ts b/src/utils.ts index b5d99c0..efe5a55 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,33 +1,52 @@ -import { CompiledCircuit, WitnessMap } from "@noir-lang/noir_js"; +import { CompiledCircuit, Noir, WitnessMap } from "@noir-lang/noir_js"; import { promises as fs } from "fs"; import toml from "toml"; +import os from "os"; import { Abi, abiEncode, type InputMap } from "@noir-lang/noirc_abi"; +import { BarretenbergBackend } from "@noir-lang/backend_barretenberg"; -export async function readCircuit( - packageName: string, -): Promise { +interface CircuitData { + noir: Noir; + bb: BarretenbergBackend; + circuit: CompiledCircuit; +} + +export async function initCircuit(packageName: string): Promise { + const circuit = await readCircuit(packageName); + const bb = new BarretenbergBackend(circuit, { + threads: os.cpus().length, + }); + const noir = new Noir(circuit, bb); + + return { + noir, + circuit, + bb, + }; +} + +export async function readCircuit(packageName: string): Promise { const CIRCUIT_PATH = `./target/${packageName}.json`; const data = await fs.readFile(CIRCUIT_PATH, "utf8"); return JSON.parse(data); } -export async function readProof(path: string): Promise { - const proofHex = await fs.readFile(path, "utf-8"); +export async function readProof(packageName: string): Promise { + const PROOF_PATH = `./proofs/${packageName}.proof`; + const proofHex = await fs.readFile(PROOF_PATH, "utf-8"); return encodeHexString("0x" + proofHex); } -export async function readWitnessMap( - path: string, - abi: Abi, -): Promise { - const inputMap = await readInputMap(path); +export async function readWitnessMap(packageName: string, abi: Abi): Promise { + const inputMap = await readInputMap(packageName); const witnessMap = abiEncode(abi, inputMap, inputMap["return"]); return witnessMap; } -export async function readInputMap(path: string): Promise { - const verifierData = await fs.readFile(path, "utf-8"); +export async function readInputMap(packageName: string): Promise { + const PROVER_MAP_PATH = `./circuits/${packageName}/Prover.toml`; + const verifierData = await fs.readFile(PROVER_MAP_PATH, "utf-8"); const inputMap = toml.parse(verifierData); return inputMap; } @@ -45,10 +64,7 @@ export function encodeHexString(value: string): Uint8Array { export type Hex = `0x${string}`; -export function isHex( - value: unknown, - { strict = true }: { strict?: boolean } = {}, -): value is Hex { +export function isHex(value: unknown, { strict = true }: { strict?: boolean } = {}): value is Hex { if (!value) return false; if (typeof value !== "string") return false; return strict ? /^0x[0-9a-fA-F]*$/.test(value) : value.startsWith("0x");