Skip to content
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
44 changes: 44 additions & 0 deletions .github/workflows/recursion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Run recursion Tests on PR
on:
pull_request:
paths:
- "recursion/**"

jobs:
test:
defaults:
run:
working-directory: recursion

runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Install Nargo
uses: noir-lang/noirup@v0.1.2
with:
toolchain: stable

- name: Install bb
run: |
curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash
echo "PATH=$PATH:/home/runner/.bb" >> $GITHUB_ENV

- name: Run bbup
run: |
bbup

- name: Build circuits
run: |
./build.sh
working-directory: recursion/circuits

- name: Install JS dependencies
working-directory: recursion/js
run: |
yarn install

- name: Generate proof in JS
run: |
yarn generate-proof
working-directory: recursion/js
20 changes: 8 additions & 12 deletions recursion/.gitignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
proofs
.vscode
crs
.DS_Store
artifacts
.cache
.target
typechain-types
cache
target
.next
node_modules
next-env.d.types
out
proofs
broadcast
.env
plonk_vk.sol
circuits/**/target/*
js/node_modules
.DS_Store
4 changes: 0 additions & 4 deletions recursion/.prettierignore

This file was deleted.

7 changes: 0 additions & 7 deletions recursion/.prettierrc

This file was deleted.

Binary file removed recursion/.yarn/install-state.gz
Binary file not shown.
893 changes: 0 additions & 893 deletions recursion/.yarn/releases/yarn-4.1.0.cjs

This file was deleted.

2 changes: 0 additions & 2 deletions recursion/.yarnrc.yml

This file was deleted.

37 changes: 20 additions & 17 deletions recursion/README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
# Recursive proofs with Noir
# Noir Recursion Example

Recursive proofs mean you prove that another proof is correct. A bit of a proofinception but bear
with me:
E2E example of generating a recursive proof using Noir and bb.js.

- 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
- Circuits in `circuits/` directory
- JS code in `js/generate-proof.ts`

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.
### Version used

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.
```
Noir 1.0.0-beta.6
bb 0.84.0
```

## Getting Started
### Steps

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

## Testing
```bash
(cd circuits && ./build.sh)
```

To run the [test file](./packages/hardhat/test/index.test.ts), try `yarn test`
2. Generate inner and recursive proof, and verify the recursive proof

```bash
(cd js && yarn install)
(cd js && yarn generate-proof)
```
13 changes: 13 additions & 0 deletions recursion/circuits/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
set -e

cd inner
nargo compile
bb write_vk --oracle_hash keccak -b ./target/inner.json -o ./target
cd ..

cd recursive
nargo compile
bb write_vk --oracle_hash keccak -b ./target/recursive.json -o ./target
cd ..

echo "Done"
7 changes: 7 additions & 0 deletions recursion/circuits/inner/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name="inner"
type="bin"
authors = ["saleel"]
compiler_version = ">=1.0.0"

[dependencies]
5 changes: 5 additions & 0 deletions recursion/circuits/inner/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

fn main(x: Field, y: pub Field) {
let res = x * 2 + y;
assert(res == 9);
}
7 changes: 7 additions & 0 deletions recursion/circuits/recursive/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name="recursive"
type="bin"
authors = ["saleel"]
compiler_version = ">=1.0.0"

[dependencies]
17 changes: 17 additions & 0 deletions recursion/circuits/recursive/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
global HONK_VK_SIZE: u32 = 128;
global HONK_PROOF_SIZE: u32 = 456;
global HONK_IDENTIFIER: u32 = 1;

fn main(
verification_key: [Field; HONK_VK_SIZE],
proof: [Field; HONK_PROOF_SIZE],
public_inputs: pub [Field; 1],
) {
std::verify_proof_with_type(
verification_key,
proof,
public_inputs,
0x0,
HONK_IDENTIFIER,
);
}
38 changes: 38 additions & 0 deletions recursion/js/generate-proof.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Barretenberg, RawBuffer, UltraHonkBackend } from "@aztec/bb.js";
import innerCircuit from "../circuits/inner/target/inner.json" assert { type: "json" };
import recursiveCircuit from "../circuits/recursive/target/recursive.json" assert { type: "json" };
import { CompiledCircuit, Noir } from "@noir-lang/noir_js";

(async () => {
try {
const innerCircuitNoir = new Noir(innerCircuit as CompiledCircuit);
const innerBackend = new UltraHonkBackend(innerCircuit.bytecode, { threads: 1 }, { recursive: true });

// Generate proof for inner circuit
const inputs = { x: 3, y: 3 }
const { witness } = await innerCircuitNoir.execute(inputs);
const { proof: innerProofFields, publicInputs: innerPublicInputs } = await innerBackend.generateProofForRecursiveAggregation(witness);

// Get verification key for inner circuit as fields
const innerCircuitVerificationKey = await innerBackend.getVerificationKey();
const barretenbergAPI = await Barretenberg.new({ threads: 1 });
const vkAsFields = (await barretenbergAPI.acirVkAsFieldsUltraHonk(new RawBuffer(innerCircuitVerificationKey))).map(field => field.toString());

// Generate proof of the recursive circuit
const recursiveCircuitNoir = new Noir(recursiveCircuit as CompiledCircuit);
const recursiveBackend = new UltraHonkBackend(recursiveCircuit.bytecode, { threads: 1 });

const recursiveInputs = { proof: innerProofFields, public_inputs: innerPublicInputs, verification_key: vkAsFields };
const { witness: recursiveWitness } = await recursiveCircuitNoir.execute(recursiveInputs);
const { proof: recursiveProof, publicInputs: recursivePublicInputs } = await recursiveBackend.generateProof(recursiveWitness);

// Verify recursive proof
const verified = await recursiveBackend.verifyProof({ proof: recursiveProof, publicInputs: recursivePublicInputs });
console.log("Recursive proof verified: ", verified);

process.exit(verified ? 0 : 1);
} catch (error) {
console.error(error);
process.exit(1);
}
})();
17 changes: 17 additions & 0 deletions recursion/js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "js",
"type": "module",
"scripts": {
"generate-proof": "tsx ./generate-proof.ts"
},
"dependencies": {
"@aztec/bb.js": "0.84.0",
"@noir-lang/noir_js": "1.0.0-beta.6"
},
"devDependencies": {
"@types/node": "^22.10.1",
"tsx": "^4.19.3",
"typescript": "^5.7.2"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
27 changes: 27 additions & 0 deletions recursion/js/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"resolveJsonModule": true,
"esModuleInterop": true,
"allowJs": true,
"strict": true,
"skipLibCheck": true,
"outDir": "./dist",
"baseUrl": "."
},
"include": [
"./**/*.ts",
"./**/*.json"
],
"exclude": [
"node_modules",
"dist"
],
"ts-node": {
"compilerOptions": {
"module": "NodeNext"
}
}
}
Loading