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: Release Noir() #3855

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
".": "0.22.0",
"acvm-repo": "0.38.0"
"acvm-repo": "0.39.0"
}
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ repository = "https://github.com/noir-lang/noir/"
[workspace.dependencies]

# ACVM workspace dependencies
acir_field = { version = "0.38.0", path = "acvm-repo/acir_field", default-features = false }
acir = { version = "0.38.0", path = "acvm-repo/acir", default-features = false }
acvm = { version = "0.38.0", path = "acvm-repo/acvm" }
acir_field = { version = "0.39.0", path = "acvm-repo/acir_field", default-features = false }
acir = { version = "0.39.0", path = "acvm-repo/acir", default-features = false }
acvm = { version = "0.39.0", path = "acvm-repo/acvm" }
stdlib = { version = "0.37.1", package = "acvm_stdlib", path = "acvm-repo/stdlib", default-features = false }
brillig = { version = "0.38.0", path = "acvm-repo/brillig", default-features = false }
brillig_vm = { version = "0.38.0", path = "acvm-repo/brillig_vm", default-features = false }
acvm_blackbox_solver = { version = "0.38.0", path = "acvm-repo/blackbox_solver", default-features = false }
barretenberg_blackbox_solver = { version = "0.38.0", path = "acvm-repo/barretenberg_blackbox_solver", default-features = false }
brillig = { version = "0.39.0", path = "acvm-repo/brillig", default-features = false }
brillig_vm = { version = "0.39.0", path = "acvm-repo/brillig_vm", default-features = false }
acvm_blackbox_solver = { version = "0.39.0", path = "acvm-repo/blackbox_solver", default-features = false }
barretenberg_blackbox_solver = { version = "0.39.0", path = "acvm-repo/barretenberg_blackbox_solver", default-features = false }

# Noir compiler workspace dependencies
arena = { path = "compiler/utils/arena" }
Expand Down
24 changes: 24 additions & 0 deletions acvm-repo/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.39.0](https://github.com/noir-lang/noir/compare/v0.38.0...v0.39.0) (2023-12-18)


### ⚠ BREAKING CHANGES

* Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841))
* Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805))

### Features

* Aztec-packages ([#3754](https://github.com/noir-lang/noir/issues/3754)) ([c043265](https://github.com/noir-lang/noir/commit/c043265e550b59bd4296504826fe15d3ce3e9ad2))
* Speed up transformation of debug messages ([#3815](https://github.com/noir-lang/noir/issues/3815)) ([2a8af1e](https://github.com/noir-lang/noir/commit/2a8af1e4141ffff61547ee1c2837a6392bd5db48))


### Bug Fixes

* Deserialize odd length hex literals ([#3747](https://github.com/noir-lang/noir/issues/3747)) ([4000fb2](https://github.com/noir-lang/noir/commit/4000fb279221eb07187d657bfaa7f1c7b311abf2))


### Miscellaneous Chores

* Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805)) ([0383100](https://github.com/noir-lang/noir/commit/0383100853a80a5b28b797cdfeae0d271f1b7805))
* Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) ([9e5d0e8](https://github.com/noir-lang/noir/commit/9e5d0e813d61a0bfb5ee68174ed287c5a20f1579))

## [0.38.0](https://github.com/noir-lang/noir/compare/v0.37.1...v0.38.0) (2023-12-18)


Expand Down Expand Up @@ -58,7 +82,7 @@

* **acvm_js:** Export black box solver functions ([#2812](https://github.com/noir-lang/noir/issues/2812)) ([da8a98e](https://github.com/noir-lang/noir/commit/da8a98ed312fe69cb0bdb8f9d0a70ee7a981398f))
* **acvm:** Separate ACVM optimizations and transformations ([#2979](https://github.com/noir-lang/noir/issues/2979)) ([5865d1a](https://github.com/noir-lang/noir/commit/5865d1a1bca16e1853663c71f893ff81fa3f7185))
* Add `FieldElement::from<usize>` implementation ([#3647](https://github.com/noir-lang/noir/issues/3647)) ([8b7c5aa](https://github.com/noir-lang/noir/commit/8b7c5aa5311f4e6811438f67bd552b641b13fc9a))

Check warning on line 85 in acvm-repo/CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (usize)
* Add ACIR serializer C++ codegen ([#2961](https://github.com/noir-lang/noir/issues/2961)) ([7556982](https://github.com/noir-lang/noir/commit/7556982dbebe25eaa17240abbe270b771b55de45))
* Add conditional compilation of methods based on the underlying field being used ([#3045](https://github.com/noir-lang/noir/issues/3045)) ([2e008e2](https://github.com/noir-lang/noir/commit/2e008e2438795bbc41b0641e830378b76bf2e194))
* Add debugger commands to introspect (and modify) the current state ([#3391](https://github.com/noir-lang/noir/issues/3391)) ([9e1ad85](https://github.com/noir-lang/noir/commit/9e1ad858cf8a1d9aba0137abe6a749267498bfaf))
Expand Down Expand Up @@ -108,7 +132,7 @@

* **acvm_js:** Export black box solver functions ([#2812](https://github.com/noir-lang/noir/issues/2812)) ([da8a98e](https://github.com/noir-lang/noir/commit/da8a98ed312fe69cb0bdb8f9d0a70ee7a981398f))
* **acvm:** Separate ACVM optimizations and transformations ([#2979](https://github.com/noir-lang/noir/issues/2979)) ([5865d1a](https://github.com/noir-lang/noir/commit/5865d1a1bca16e1853663c71f893ff81fa3f7185))
* Add `FieldElement::from<usize>` implementation ([#3647](https://github.com/noir-lang/noir/issues/3647)) ([8b7c5aa](https://github.com/noir-lang/noir/commit/8b7c5aa5311f4e6811438f67bd552b641b13fc9a))

Check warning on line 135 in acvm-repo/CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (usize)
* Add ACIR serializer C++ codegen ([#2961](https://github.com/noir-lang/noir/issues/2961)) ([7556982](https://github.com/noir-lang/noir/commit/7556982dbebe25eaa17240abbe270b771b55de45))
* Add conditional compilation of methods based on the underlying field being used ([#3045](https://github.com/noir-lang/noir/issues/3045)) ([2e008e2](https://github.com/noir-lang/noir/commit/2e008e2438795bbc41b0641e830378b76bf2e194))
* Add debugger commands to introspect (and modify) the current state ([#3391](https://github.com/noir-lang/noir/issues/3391)) ([9e1ad85](https://github.com/noir-lang/noir/commit/9e1ad858cf8a1d9aba0137abe6a749267498bfaf))
Expand Down Expand Up @@ -541,11 +565,11 @@

### ⚠ BREAKING CHANGES

* Provide runtime callstacks for brillig failures and return errors in acvm_js ([#523](https://github.com/noir-lang/acvm/issues/523))

Check warning on line 568 in acvm-repo/CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (callstacks)

### Features

* Provide runtime callstacks for brillig failures and return errors in acvm_js ([#523](https://github.com/noir-lang/acvm/issues/523)) ([7ab7cff](https://github.com/noir-lang/acvm/commit/7ab7cff48a9aba61a97fad2a759fc8e55740b098))

Check warning on line 572 in acvm-repo/CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (callstacks)


### Bug Fixes
Expand Down Expand Up @@ -594,7 +618,7 @@

### Features

* **acvm_js:** Add `execute_circuit_with_black_box_solver` to prevent reinitialization of `BlackBoxFunctionSolver` ([3877e0e](https://github.com/noir-lang/acvm/commit/3877e0e438a8d0e5545a4da7210767dec05c342f))

Check warning on line 621 in acvm-repo/CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (reinitialization)
* Expose a `BlackBoxFunctionSolver` containing a barretenberg wasm from `blackbox_solver` ([#494](https://github.com/noir-lang/acvm/issues/494)) ([a1d4b71](https://github.com/noir-lang/acvm/commit/a1d4b71256dfbf1e883e770dd9c45479235aa860))


Expand Down Expand Up @@ -648,7 +672,7 @@

### Features

* add optimisations to fallback black box functions on booleans ([#446](https://github.com/noir-lang/acvm/issues/446)) ([2cfb2a8](https://github.com/noir-lang/acvm/commit/2cfb2a8cf911a81eedbd9da13ab2c616abd67f83))

Check warning on line 675 in acvm-repo/CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (optimisations)
* **stdlib:** Add fallback implementation of `Keccak256` black box function ([#445](https://github.com/noir-lang/acvm/issues/445)) ([f7ebb03](https://github.com/noir-lang/acvm/commit/f7ebb03653c971f119700ff8126d9eb5ff01be0f))

## [0.20.0](https://github.com/noir-lang/acvm/compare/root-v0.19.1...root-v0.20.0) (2023-07-20)
Expand Down Expand Up @@ -702,15 +726,15 @@

### ⚠ BREAKING CHANGES

* add backend-solvable blackboxes to brillig & unify implementations ([#422](https://github.com/noir-lang/acvm/issues/422))

Check warning on line 729 in acvm-repo/CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (blackboxes)
* **acvm:** Remove `CircuitSimplifer` ([#421](https://github.com/noir-lang/acvm/issues/421))

Check warning on line 730 in acvm-repo/CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (Simplifer)
* **acvm:** Add `circuit: &Circuit` to `eth_contract_from_vk` function signature ([#420](https://github.com/noir-lang/acvm/issues/420))
* Returns index of failing opcode and transformation mapping ([#412](https://github.com/noir-lang/acvm/issues/412))

### Features

* **acvm:** Add `circuit: &Circuit` to `eth_contract_from_vk` function signature ([#420](https://github.com/noir-lang/acvm/issues/420)) ([744e9da](https://github.com/noir-lang/acvm/commit/744e9da71f7ca477a5390a63f47211dd4dffb8b3))
* add backend-solvable blackboxes to brillig & unify implementations ([#422](https://github.com/noir-lang/acvm/issues/422)) ([093342e](https://github.com/noir-lang/acvm/commit/093342ea9481a311fa71343b8b7a22774788838a))

Check warning on line 737 in acvm-repo/CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (blackboxes)
* derive PartialOrd, Ord, and Hash on RegisterIndex ([#425](https://github.com/noir-lang/acvm/issues/425)) ([7f6b0dc](https://github.com/noir-lang/acvm/commit/7f6b0dc138c4e11d2b5847f0c9603979cc43493a))
* Returns index of failing opcode and transformation mapping ([#412](https://github.com/noir-lang/acvm/issues/412)) ([79950e9](https://github.com/noir-lang/acvm/commit/79950e943f60e4082e1cf5ec4442aa67ea91aade))
* **stdlib:** Add fallback implementation of `SHA256` black box function ([#407](https://github.com/noir-lang/acvm/issues/407)) ([040369a](https://github.com/noir-lang/acvm/commit/040369adc8749fa5ec2edd255ff54c105c3140f5))
Expand All @@ -718,7 +742,7 @@

### Miscellaneous Chores

* **acvm:** Remove `CircuitSimplifer` ([#421](https://github.com/noir-lang/acvm/issues/421)) ([e07a56d](https://github.com/noir-lang/acvm/commit/e07a56d9c542a7f03ce156761054cd403de0bd23))

Check warning on line 745 in acvm-repo/CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (Simplifer)

## [0.17.0](https://github.com/noir-lang/acvm/compare/root-v0.16.0...root-v0.17.0) (2023-07-07)

Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/acir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "acir"
description = "ACIR is the IR that the VM processes, it is analogous to LLVM IR"
# x-release-please-start-version
version = "0.38.0"
version = "0.39.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/acir_field/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "acir_field"
description = "The field implementation being used by ACIR."
# x-release-please-start-version
version = "0.38.0"
version = "0.39.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/acvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "acvm"
description = "The virtual machine that processes ACIR given a backend/proof system."
# x-release-please-start-version
version = "0.38.0"
version = "0.39.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/acvm_js/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "acvm_js"
description = "Typescript wrapper around the ACVM allowing execution of ACIR code"
# x-release-please-start-version
version = "0.38.0"
version = "0.39.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/acvm_js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@noir-lang/acvm_js",
"version": "0.38.0",
"version": "0.39.0",
"repository": {
"type": "git",
"url": "https://github.com/noir-lang/acvm.git"
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/barretenberg_blackbox_solver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "barretenberg_blackbox_solver"
description = "A wrapper around a barretenberg WASM binary to execute black box functions for which there is no rust implementation"
# x-release-please-start-version
version = "0.38.0"
version = "0.39.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/blackbox_solver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "acvm_blackbox_solver"
description = "A solver for the blackbox functions found in ACIR and Brillig"
# x-release-please-start-version
version = "0.38.0"
version = "0.39.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/brillig/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "brillig"
description = "Brillig is the bytecode ACIR uses for non-determinism."
# x-release-please-start-version
version = "0.38.0"
version = "0.39.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/brillig_vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "brillig_vm"
description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM"
# x-release-please-start-version
version = "0.38.0"
version = "0.39.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
Expand Down
175 changes: 175 additions & 0 deletions docs/versioned_docs/version-v0.22.0/explainers/explainer-recursion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
---
title: Recursive proofs
description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency.

keywords:
[
"Recursive Proofs",
"Zero-Knowledge Programming",
"Noir",
"EVM Blockchain",
"Smart Contracts",
"Recursion in Noir",
"Alice and Bob Guessing Game",
"Recursive Merkle Tree",
"Reusable Components",
"Optimizing Computational Resources",
"Improving Efficiency",
"Verification Key",
"Aggregation Objects",
"Recursive zkSNARK schemes",
"PLONK",
"Proving and Verification Keys"
]
sidebar_position: 1
---

In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number:

```js
function factorial(n) {
if (n === 0 || n === 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}
```

In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack:

```md
Is `n` 1? <---------
/\ /
/ \ n = n -1
/ \ /
Yes No --------
```

In Zero-Knowledge, recursion has some similarities.

It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid.

This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism.

## Examples

Let us look at some of these examples

### Alice and Bob - Guessing game

Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins.

So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed.

This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it.

So, Alice started thinking: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". She can then generate a proof that she verified his proof, and so on.

```md
Did you fail? <--------------------------
/ \ /
/ \ n = n -1
/ \ /
Yes No /
| | /
| | /
| You win /
| /
| /
Generate proof of that /
+ /
my own guess ----------------
```

### Charlie - Recursive merkle tree

Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count!

So, the tallier puts all the votes in a merkle tree, and everyone can also prove the verification of two proofs within one proof, as such:

```md
abcd
__________|______________
| |
ab cd
_____|_____ ______|______
| | | |
alice bob charlie daniel
```

Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes.

### Daniel - Reusable components

Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit.

He could find it more efficient to generate a proof for that setup phase separately, and verifying it in his actual business logic section of the circuit. This will allow for parallelization of both proofs, which results in a considerable speedup.

## What params do I need

As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires:

- The proof to verify
- The Verification Key of the circuit that generated the proof
- A hash of this verification key, as it's needed for some backends
- The public inputs for the proof
- The input aggregation object

It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit.

### Aggregation objects

Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs.

In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points.

So, taking the example of Alice and Bob and their guessing game:

- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit
- Bob verifies Alice's proof and makes his own guess. In this circuit, he is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof!
- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`.

One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead.

We can imagine the `aggregation object` as the baton in a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid.

## Some architecture

As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples:

### Adding some logic to a proof verification

This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections:

- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify)
- A `guessing` section, which is basically the logic part where the actual guessing happens

In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on.

### Aggregating proofs

In some one-way interaction situations, recursiveness would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere.

To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits:

- A `main`, non-recursive circuit with some logic
- A `recursive` circuit meant to verify two proofs in one proof

The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day.

### Recursively verifying different circuits

Nothing prevents you from verifying different circuits in a recursive proof, for example:

- A `circuit1` circuit
- A `circuit2` circuit
- A `recursive` circuit

In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received)

## How fast is it

At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later.

Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package.