Noir Circuits
This page is for developers who build, change, or reuse the Noir circuits in the circuits/
directory of the Interfold repository. It shows where each package lives and which tool versions to
install. It also lists the scripts that build the circuits and the Solidity verifiers, and it shows
how the Rust prover finds the artifacts. At the end, you can build the circuits for a preset and a
committee size. You can also use the circuit library in your own Noir project.
For what the circuits prove (the PV-TBFV phases and the circuit IDs C0 to C7), read Cryptography first. The Noir sources and their tests are the source of truth for constraints and public inputs.
Workspace layout
All binary packages depend on one shared library. The Nargo package name of the library is lib
(circuits/lib/Nargo.toml).
- Nargo.toml
- lib.nr
| Directory | Contents |
|---|---|
lib/src/math | Polynomials, the SAFE sponge (Poseidon2 and Keccak256), modular arithmetic, and the commitment functions with their domain separators. |
lib/src/core | The circuit logic that the binary packages call. core/dkg holds C0 and C2 to C4. core/threshold holds C1, C5, the two user-encryption legs, C6, and C7. |
lib/src/configs | The BFV presets (insecure, secure) and the committee sizes. default/mod.nr and committee/active.nr select the active pair. The build script writes both files. |
bin/dkg | Nargo workspace: pk (C0), sk_share_computation (C2a), e_sm_share_computation (C2b), share_encryption (C3), share_decryption (C4). |
bin/threshold | Nargo workspace: pk_generation (C1), pk_aggregation (C5), user_data_encryption_ct0 and user_data_encryption_ct1 with their wrapper user_data_encryption (P3), share_decryption (C6), decrypted_shares_aggregation (C7). |
bin/recursive_aggregation | Twelve packages that fold proofs together. The two final circuits are dkg_aggregator and decryption_aggregator. |
bin/config | Package validate_secure_configs. It checks the secure preset constants (CRT moduli, bounds, and parity matrices). |
benchmarks | Timing scripts and reports. |
The package directory names match the CircuitName enum in crates/zk/config/src/circuit.rs. For
example, CircuitName::PkBfv is pk, and CircuitName::DkgAggregator is dkg_aggregator. The
full package index is in
circuits/README.md (opens in a new tab).
How the proofs combine
Each ciphernode proves its DKG steps with the inner circuits. The recursive aggregation packages fold these proofs, first for each ciphernode and then across the committee. Only the two aggregator proofs go on-chain.
- Inner proofsC0 to C4 for each ciphernode.
- Step foldsc2ab_fold, c3_fold, c3ab_fold, c4ab_fold.
- node_foldOne fold for each ciphernode.
- nodes_foldFolds the node_fold proofs of the committee.
- dkg_aggregatorAdds C5. Verified on-chain.
The decryption path is shorter. The C6 proofs of the committee go through c6_fold, and
decryption_aggregator adds C7. The *_kernel packages make the first proof of each sequential
fold (c3_fold, c6_fold, nodes_fold).
The Solidity verifiers for the two aggregators are committed to the repository as
DkgAggregatorVerifier.sol and DecryptionAggregatorVerifier.sol. BfvPkVerifier and
BfvDecryptionVerifier hold the address of one of these verifiers and call it through
ICircuitVerifier.
Toolchain
A circuit build needs two tools: nargo compiles Noir, and bb (Barretenberg) writes verification
keys, proofs, and Solidity verifiers. Use the pinned versions. Artifacts from other versions do not
match the prover or the committed verifiers.
This table describes repository source builds. Released binaries keep the versions pinned at their release.
| Component | Version | Pinned in |
|---|---|---|
nargo | 1.0.0-rc.3 | NOIR_TOOLCHAIN in .github/workflows/ci.yml. The Noir crates in crates/zk/prover/Cargo.toml use the same tag. |
bb | 6.0.0 | required_bb_version in crates/zk/prover/versions.json, and BB_VERSION in ci.yml |
| Circuit artifacts for source builds | Build from this checkout | pnpm build:circuits |
bb_proof_verification (Noir) | v6.0.0 | The Nargo.toml of each package that verifies a proof |
@aztec-foundation/bb.js, @noir-lang/noir_js | 6.0.0, 1.0.0-rc.3 | packages/interfold-sdk/package.json |
For a source build, compile the circuits from the same checkout. interfold noir setup installs the
CLI's release archive; it does not compile the checkout. Do not reuse an archive built with a
different toolchain. A release needs a matching archive before nodes can use it.
Install nargo
Install noirup, then use it to install the pinned compiler:
curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash
export PATH="$PATH:$HOME/.nargo/bin"
noirup -v v1.0.0-rc.3The installer adds ~/.nargo/bin to your shell profile. The export line makes it available in the
current shell.
Install bb
For this checkout, install the binary for your platform from the Barretenberg 6.0.0 release (opens in a new tab).
For a released node, run interfold noir setup. It downloads the CLI's pinned bb, checks it
against the checksum in versions.json, and installs that release's circuit archive:
interfold noir setupWithout an Interfold config file, the CLI installs into ~/.interfold/noir: bin/bb, circuits/,
and version.json. With a config file, it uses .interfold/noir next to that file.
Put bb on your PATH
The repository scripts call bb from your PATH. For a source build, add the directory with the
6.0.0 binary. To use the copy installed by a matching released CLI, add its directory:
export PATH="$HOME/.interfold/noir/bin:$PATH"Check the versions
nargo --version
bb --versionFor source builds, nargo reports 1.0.0-rc.3 and bb reports 6.0.0.
interfold noir commands
interfold noir manages the bb binary and the circuit artifacts that a ciphernode uses
(crates/cli/src/noir.rs).
| Command | What it does |
|---|---|
interfold noir status | Prints the path and version of bb and of the circuits, then the status: Ready, Barretenberg needs update, Circuits need update, or Setup required. |
interfold noir setup | Installs what is missing or out of date. If everything matches versions.json, it prints the versions and stops. |
interfold noir setup --force | Downloads bb and the circuit archive again. |
interfold noir setup --circuits-archive <PATH> | Installs the circuits from a local release archive instead of the download. |
The circuit archive comes from the GitHub release pinned in required_circuits_version. The node
selects the artifact directory from the BFV parameter set and the committee size of each E3.
Build the circuits in the repository
Run the build from the repository root. It needs nargo and bb on your PATH, the Rust toolchain
(the script runs two Rust generators), and the JavaScript dependencies (pnpm install).
pnpm build:circuits # insecure-64, minimum
pnpm build:circuits --preset secure-8192 --committee smallA build selects one BFV preset and one committee size:
| Option | Values | Default |
|---|---|---|
--preset | insecure-64, secure-8192, all | insecure-64 |
--committee | minimum, micro, small, all | minimum |
| Committee | N | T | H |
|---|---|---|---|
minimum | 3 | 1 | 2 |
micro | 9 | 4 | 5 |
small | 19 | 9 | 14 |
N is the committee size, T is the threshold, and H is the number of honest parties that the DKG
expects. Decryption needs T + 1 shares. The mainnet contracts accept only the secure parameter set.
Sepolia and local chains accept both sets (ActiveCryptoConfig.sol).
For each pair, the Rust build owner does these steps (crates/zk/build):
- It writes
configs/default/mod.nrandconfigs/committee/active.nrfor the pair. - It regenerates the parity matrices and the C1 and C2 bounds with the Rust generators.
- It updates
packages/interfold-contracts/scripts/utils.tsandpackages/interfold-contracts/contracts/lib/BfvProtocol.sol. - It compiles every package in
bin/dkg,bin/threshold, andbin/recursive_aggregationwithnargo compile. - It writes verification keys with
bb write_vk. Each base circuit getsevm,noir-recursive-no-zk, andnoir-recursivekeys. Each fold circuit gets anoir-recursive-no-zkkey, and the two aggregators also get anevmkey. - It copies the artifacts to
dist/circuits/<preset>/<committee>/, with one directory for each variant (default,recursive,evm). Then it writesSHA256SUMSandchecksums.json.
The build records the active pair in circuits/bin/.active-preset.json.
Downloaded release archives must match the SHA-256 compiled into the binary for their version. The
binary includes pins from crates/zk/prover/versions.json (circuits_checksums) and the release
build input. The installer checks it before extraction, then requires a nonempty internal manifest
that covers every artifact file. Root manifests, SOURCE_HASH, and build stamps are metadata.
noir setup --circuits-archive requires the same pin for a local archive. Add
--allow-unpinned-archive to install another archive, such as a local build.
The installer requires every pair in crates/zk/prover/supported-configurations.json by default,
including for noir setup --circuits-archive. The release validator uses the same matrix. For a
local archive, repeat --circuits-configuration <PRESET/COMMITTEE> to select a subset explicitly.
For example, --circuits-configuration insecure-64/minimum selects the development configuration.
Local and CI callers can request a nonempty subset through download_circuits_for_configurations or
install_circuits_archive_for_configurations. The required set never depends on archive contents.
Each required or included pair must contain every artifact in
crates/zk/prover/required-artifacts.json. The release validator and installer share this
inventory. The manifest must cover each artifact.
If circuit replacement or the version update fails, the installer attempts rollback and returns the original installation error. Each rollback failure logs the source and target paths. If the previous circuits cannot be restored, the installer keeps the staging directory and logs its path for recovery.
The release workflow packages the archive first and passes its SHA-256 as
E3_CIRCUITS_ARCHIVE_SHA256 to binary builds and the ciphernode Docker build. The build binds this
pin to the crate version. Each binary and image build checks the pin reported by
interfold noir status before publication. Builds without this input still work. A download for an
unpinned version fails with the missing archive name. The workflow publishes the exact archive that
it hashed. Repacking changes the digest even when artifact bytes stay the same. SOURCE_HASH
identifies source inputs and is not an archive digest.
The preset and the committee size stay selected after the build, and they change tracked files.
Always switch them with pnpm build:circuits --preset <name> --committee <name>. Do not edit
active.nr, utils.ts, or a parity_*.nr file by hand. The pre-push hook runs
pnpm check:committee, which rejects files that disagree. Circuit artifacts and verifiers are
specific to the pair, so a changed H needs new circuits and new verifiers.
Other circuit commands:
| Command | What it does |
|---|---|
pnpm build:circuits --dry-run | Lists the circuits that a build compiles, and stops. |
pnpm build:circuits --group dkg --skip-vk | Compiles one group and skips the verification keys. |
pnpm build:circuits hash | Prints the source hash that identifies a build. |
pnpm build:circuits sync-config --preset <p> --committee <c> | Updates utils.ts and BfvProtocol.sol only. It does not compile. |
pnpm check:committee | Checks that the committee and BFV values agree across Noir, Rust, TypeScript, and Solidity. |
pnpm noir:lint (scripts/circuits/lint.sh) | Runs nargo fmt --check and nargo check on the library and each package. It exits with success if nargo is missing. |
pnpm noir:test (scripts/circuits/test.sh) | Runs nargo test on circuits/lib and decryption_aggregator, then nargo execute on bin/config, then the dkg_aggregator and node_fold tests for three preset and committee pairs. |
The Rust benchmark runner creates artificial cryptographic material and executes production witness preparation directly. Its preset and committee must match the selected artifacts:
pnpm bench:circuits run --mode insecure --committee minimum --circuit threshold/pk_generation --witness-only --leaf-onlyUse --export-toml only for debug ABI inputs. Samples are not live application inputs.
pnpm bench:circuits list prints the selected leaf cases. The Rust configuration library owns
static configuration; pnpm build:circuits sync-preset writes the selected preset's constants.
Generate the Solidity verifiers
The Honk verifiers for dkg_aggregator and decryption_aggregator are committed under
packages/interfold-contracts/contracts/verifiers/bfv/honk/. The files at the top of that directory
are for insecure-64 with minimum. Every other pair has its own directory,
honk/<preset>/<committee>/.
pnpm build:circuits --preset insecure-64 --committee minimum
pnpm generate:verifiers # rewrite the committed verifiers
pnpm check:verifiers # compare them with the current keys, write nothinggenerate:verifiers compiles each circuit if necessary, runs bb write_vk -t evm and
bb write_solidity_verifier, and renames the contract from HonkVerifier to a name such as
DkgAggregatorVerifier. It refuses to run when the build stamp in dist/circuits or
circuits/bin/.active-preset.json does not match the requested pair. For another pair, add
--preset <name> --committee <name>.
Each generated verifier has this function, the same as ICircuitVerifier
(contracts/interfaces/ICircuitVerifier.sol):
function verify(
bytes calldata _proof,
bytes32[] calldata _publicInputs
) external view returns (bool);Verifiers for your own circuits
An E3 program can check its inputs with its own circuits. CRISP does this in
scripts/compile_circuits.sh (opens in a new tab)
in the CRISP repository. The user_data_encryption circuits and their keys come compiled from the
Interfold release. For each ballot stack, the script does these steps:
- It compiles the application circuit and writes its verification key with
bb write_vk -t noir-recursive-no-zk. - It compiles the fold circuit, which verifies both proofs, and writes its key with
bb write_vk --oracle_hash keccak. - It runs
bb write_solidity_verifieron the fold key and copies the result topackages/crisp-contracts/contracts/verifiers/.
The script makes two verifiers: CRISPVerifier.sol from the crisp and fold circuits, and
CRISPOnchainVerifier.sol from crisp_onchain and fold_onchain. The verifiers do not depend on
the BFV preset. The fold circuit accepts the key hashes of both presets.
The custom circuits tutorial follows these steps for a new circuit.
The SDK function generateProof() proves only the three user-data-encryption circuits. It embeds
artifacts built for insecure-64 with minimum. See the SDK page.
Use circuits/lib in another Noir project
Add the library as a dependency in your Nargo.toml. Use the release tag that matches the prover
and the circuits that your deployment uses. CRISP uses the name interfold_lib:
[dependencies]
interfold_lib = { git = "https://github.com/theinterfold/interfold", tag = "v0.18.0", directory = "circuits/lib" }Then import from it by that name:
use interfold_lib::configs::default::threshold::{L, N};
use interfold_lib::math::commitments::compute_ciphertext_commitment;
use interfold_lib::math::polynomial::Polynomial;The library at the tag v0.18.0 selects insecure-64 with minimum. The build script writes
that selection into the source. For another pair, use a path dependency on a local checkout.
To use another pair, run pnpm build:circuits for the pair in the checkout, then point the
dependency at it:
[dependencies]
interfold_lib = { path = "<checkout>/circuits/lib" }The library itself depends on poseidon v0.3.0 and keccak256 v0.1.2 from noir-lang, and on
bignum v0.4.1 from gnosisguild/noir-bignum.
Rust integration
The crate e3-zk-prover (crates/zk/prover) runs the circuits for the ciphernode.
| Item | Role |
|---|---|
ZkBackend | Finds bb and the circuit directory. check_status, ensure_installed, download_bb, and download_circuits implement interfold noir. |
ZkProver | Runs bb prove and bb verify as a subprocess. It reads artifacts from <circuits>/<preset>/<committee>/<variant>/. |
Provable | The trait for one circuit. It names the CircuitName and builds the witness inputs. |
CircuitVariant (e3-zk-config) | Default (noir-recursive-no-zk), Recursive (noir-recursive), and Evm (evm). These are the directory names in the archive. |
ProofRequestActor, ProofVerificationActor, ShareVerificationActor | The actors that request proofs and check the proofs of other ciphernodes. |
Circuit identity/variants belong to crates/zk/config; ProofType and proof payloads remain in
crates/events. The protocol flow that uses them is in
proof statements (opens in a new tab)
and DKG (opens in a new tab).
Add or rename a circuit
- Put the package in
bin/dkg,bin/threshold, orbin/recursive_aggregation. The build script finds packages in these groups automatically. - Add the package to the
memberslist of the group workspace, forbin/dkgandbin/threshold. - Add the directory to
DIRSinscripts/circuits/lint.sh, for a package inbin/recursive_aggregation. - Add a
CircuitNamevariant incrates/zk/configif the Rust prover must run the circuit. - Rebuild each pair that you ship, and regenerate the verifiers if the circuit goes on-chain.