The circuit and its verification key
Privacy Guarantees introduces a circuit as the mathematical constraints expressing the rules to be proven. MIP410’s circuits are written in Noir and compiled into a form the proving software can execute. The proving software, Barretenberg, generates a corresponding verification key for UltraHonk, the proof system MIP410 uses, which lets the verifier check a proof that the private inputs satisfy those rules for the supplied public inputs. The key contains numerical parameters derived from the circuit’s fixed structure. It can be reused to verify proofs for many transactions, each with its own witness and public inputs. The circuit verification key has a different role from the spend-verification keyak introduced in Keys and Addresses.
Inside the circuit, ak verifies the spending-key signature authorizing the
payment. The verifier uses the circuit verification key to check the resulting
proof, including that signature check. This verification key is public and shared across wallets using that circuit.
Verification requires the circuit’s verification key, the proof, and its public
inputs. The key supplies the circuit-specific parameters UltraHonk needs to check
the proof without receiving the witness or executing the circuit on private
payment data. Metal supplies these parameters to a shared verifier that serves
different circuits. To ensure each transaction is checked against the expected
circuit, MIP410 configures circuit IDs for shielding, payment initiation,
payment resolution, unshielding, and policy-wrapper verification. Each circuit
ID identifies the backend and exact verification-key bytes. When requesting verification,
the pool selects the circuit ID required for that proof type.
Constructing a proof for a payment
For payment initiation, the prover assembles the input note openings and Merkle paths, the recipient and change note details, and the spending authorization. Its private inputs also include the spend-verification key and nullifier key, sanctions evidence, the identifier of the recipient virtual asset service provider (VASP), and the payment payload. The prover also supplies the public inputs against which the proof will be verified. These include the chain ID, pool contract address, the note tree’s Merkle root, input lock IDs, payment effect hash, recipient VASP binding commitment, nonce, deadline, payload commitment, and sanctions root. The payload commitment is a hash binding the private payment record and associated identifiers to the payment. The proving software executes the compiled circuit with these inputs to compute the witness values needed for proof generation. The circuit enforces the relationships between them:
Barretenberg uses the compiled circuit and completed witness to generate the
UltraHonk proof. The submitter includes the proof in the initiation transaction.
The private inputs remain offchain. Initiation also requires a separate
policy-wrapper proof, described in
Privacy-Preserving Policy.
Onchain verification
The pool constructs the public inputs used to verify each submitted proof. The chain ID and pool contract address come from the execution environment. Other inputs come from the transaction parameters and, where applicable, the pool’s stored records. For example, the recipient proof used for resolution is checked against values recorded in the payment’s hold. The pool selects the verification key configured for the required proof type. It supplies the corresponding circuit ID to the Circuit Registry, which retrieves the registered key. The registry forwards the key, proof, and public inputs to Metal’s native UltraHonk verifier at0xF2. Validators
perform this verification as part of executing the transaction.
The pool also checks whether its current state permits the transaction.
For transactions that use existing notes, the pool checks their availability
against its onchain records. A payment resolution requires a hold that is still
pending and within its deadline. The pool also checks that roots referenced by
the proofs are accepted. A transaction succeeds only when all required proofs
and execution checks pass. Payment initiation requires a sender proof, while
resolution requires a recipient proof. Both stages also require a policy-wrapper
proof. Each proof is
verified using its own configured circuit ID and public inputs. If any required
proof or check fails, the transaction reverts and the pool’s changes are rolled
back.