noir
Use when building, reviewing, or shipping a privacy app with Noir circuits verified onchain. The boundaries agents get wrong unprompted — in-circuit hash choice, in-browser proving with NoirJS, note persistence and the offchain tree mirror, and who sends the transaction.
- License 许可
- AGPL-3.0
- 最近更新
- 2026-11-11
- Stars
- ★ 80
- Forks
- 17
01 前言 Front Matter
- name
- noir
- description
- Use when building, reviewing, or shipping a privacy app with Noir circuits verified onchain. The boundaries agents get wrong unprompted — in-circuit hash choice, in-browser proving with NoirJS, note persistence and the offchain tree mirror, and who sends the transaction.
02 资源文件 2
- 参考文档SOURCE.md341 B
- 参考文档TRUST.auto.yaml2.1 KB
03 SKILL.md 正文
Privacy Apps with Noir
A sound circuit is not a private app. These are the boundaries around it, where the failures actually happen.
Hash in-circuit with Poseidon, never keccak or SHA256. Poseidon costs ~600 gates against ~30,000 for a bit-oriented hash, and members prove in a browser. It is not in the stdlib — add the dependency and import it:
poseidon = { git = "https://github.com/noir-lang/poseidon", tag = "v0.3.0" }
use poseidon::poseidon::bn254::hash_2 — not std::hash::poseidon (removed). Whatever you pick, every layer must use the same algorithm and input order: commitment creation, Merkle parent hashing, the offchain mirror, the onchain tree. Poseidon and Poseidon2 are different functions with different outputs. Test one leaf hash and one parent hash across every layer before building on them.
Prove in-process with NoirJS, not by shelling out to the bb CLI. A script that spawns bb prove and reads the proof off disk works on your laptop and can never become the browser prover the app needs. The packages are @noir-lang/noir_js + @aztec/bb.js (not backend_barretenberg), and the class is UltraHonkBackend (not UltraPlonkBackend):
const backend = new UltraHonkBackend(circuit.bytecode, await Barretenberg.new());
const proof = await backend.generateProof(witness, { verifierTarget: "evm" });
The API argument is required — the one-argument form docs still show throws Cannot read properties of undefined (reading 'circuitProve'). Ask for EVM serialization explicitly with { verifierTarget: "evm" } (keccak transcript, ZK); the default serialization will not verify onchain, and the older { keccak: true } is deprecated for evm-no-zk — a different target, whose proofs an evm verifier rejects. Keep @aztec/bb.js on the exact version of your bb CLI:
npm install @noir-lang/noir_js "@aztec/bb.js@$(bb --version)"
Persist the note, and rebuild the path from events. At commitment time generate a nullifier and a secret, and save them with the leafIndex — lose them and the user can never spend or vote. Anything you commit to that lives in a small domain — a bid, an age, a vote — needs one of those random fields as a blinding factor in the preimage, or the onchain commitment is brute-forceable. The client derives its Merkle witness by replaying the contract's insert events into an offchain mirror (@zk-kit/lean-imt), so the contract has to emit the commitment, its leaf index, and the resulting root. Not a contract call returning a path, and not a hardcoded one. A model that writes only the circuit forgets all of this.
Nobody's wallet should send its own proof. The ZK proof hides the commitment-nullifier link; msg.sender is public and links the two transactions anyway. Registering from the known wallet is fine when membership is public — acting from it is not. Route the acting transaction through a relayer, or send it from a fresh account whose gas an ERC-4337 paymaster sponsors — a paymaster alone hides nothing, since the UserOperation's sender is public, so reusing the registering account keeps the link. A burner funded from the user's own wallet recreates the link, and unlinkability is still bounded by the size of the anonymity set.
Also required, and usually already habit — confirm rather than rebuild:
nargo compilebuilds the circuit andnargo executeproduces the witness fromProver.toml; everything else isbb—bb prove,bb verify,bb write_vk, andbb write_solidity_verifierfed by that VK.nargo prove/nargo verify/nargo codegen-verifierwere removed. Generate the VK feeding the Solidity verifier with--verifier_target evmand keep prove, verify and VK on the same target: it names the transcript hash and the ZK setting together, which is the pair that has to match. The older--oracle_hash keccakstill works, but it names only the hash. Smoke-test a real prove-and-verify before wiring a frontend.- Public inputs use post-colon syntax:
root: pub Field. - Scope the nullifier to the action in-circuit — hash the proposal or pool id into it, or one member's votes link across proposals.
- Bind the proof to what it authorizes: make the recipient (and any amount or fee) a public input the contract checks. A proof that does not commit to the recipient can be copied from the mempool and redirected by a front-runner or a relayer.
- Keep hash domains separate: commitment and nullifier hash must not be the same function over overlapping inputs (add a domain tag or use different arities), or one value can stand in for the other.
- Every private input must be constrained by an
assertor by feeding a checked hash. An input the circuit never constrains can be set to anything, and the proof still verifies. - Count the vote or release the funds only after
verify()returns true, recording the nullifier hash so a replay reverts. Never leave aMockVerifierin a deploy path. - Public input order must match across the circuit's
pubparams, the script'spublicInputs, and the array the contract hands toverify(). Read the generated verifier's ABI rather than assuming its signature. - Merkle path indices are
[bool; DEPTH]. - Accept any recent root (
mapping(bytes32 => bool) knownRoots), not only the current one — an insert landing between proving and submitting otherwise reverts an honest proof. - The onchain tree is
@zk-kit/lean-imt.solwith a separately deployedPoseidonT3, the pair that matches the offchain@zk-kit/lean-imt. A hand-rolled onchain tree drifts from the mirror. - Next.js / Scaffold-ETH 2: set
config.experiments.asyncWebAssembly = trueinnext.config, load the prover withdynamic(..., { ssr: false }), and copy the circuit JSON topublic/andfetch()it — a cross-package JSON import fails. - The generated verifier needs
pragma solidity >=0.8.27andevm_version = 'cancun'; if the project's floor is lower, setsolc_version = '0.8.27'andevm_version = 'cancun'infoundry.toml. Build it optimized or it exceeds the 24KB limit.
Install with noirup -v <version>, then bbup -v <version> — bare bbup resolves through a version map that lags nargo and 404s. Finish by compiling the circuit and building the contracts.
04 同仓库更多 Skills
recipe-performance
Diagnose a specific MetaMask user flow's real-app performance and Sentry critical-user-flow telemetry with deterministic mm-harness recipes. Use for CUF boundaries, cold/warm/background-resume behavior, latency regressions, lifecycle tags, duplicate or blocking network requests, long tasks, layout/paint cost, screen redraws, React rerenders, or browser trace analysis where tests alone cannot prove the real app.
mexc-spot-websocket
MEXC Spot WebSocket workflows for public streams, protobuf handling, depth maintenance, listen keys, and private streams without exposing API keys.
mexc-spot-rest
MEXC Spot REST workflows for public data, signed account requests, orders, wallet/admin actions, and secure signing without exposing API keys.
mexc-futures-websocket
MEXC Futures WebSocket workflows for public streams, private account/order/position streams, ping/pong, subscriptions, and depth maintenance.
0xklen-version-and-refresh-sanctions-lists
暂无简介
verify-tests-cover-changed-lines
Use when a PR adds code that the existing suite may not exercise. Measures patch coverage — the share of newly changed lines hit by tests — before approving.