Circuits
3.1 General Safety Rule (Critical)
Public values are exposed as signal input …_pub and constrained to the computed internal signals:
anchorRoot_pub === merkle.root;
This avoids “unconstrained public output” vulnerabilities.
3.2 Shield
(public → private)
Purpose: Consume an escrowed deposit and mint up to OUT_MAX=2 private outputs (fee is allowed but we default to 0).
Parameters (v2): DEP_MAX=1, OUT_MAX=2.
Public signals:
out_commit_pub[2]
dep_ids_lo_pub[1], dep_ids_hi_pub[1], dep_amts_pub[1]
fee_pub, fee_recipient_pub
Checks:
Output commitments recomputed in‑circuit and must match.
Deposit pass‑through & value conservation in shares (sum(outputs) == sum(deposits) − fee).
Fee enforced to be zero unless you intentionally enable it.
Asset: outputs are asset‑bound (commits include assetId); you keep assetId private in shield, the pool infers asset from deposit.
3.3 Spend Anchor
(private → private/withdraw)
Purpose: Spend up to IN_MAX=3 inputs; produce up to OUT_MAX=2 outputs; optionally one withdrawal and a fee (both in shares).
Parameters (v2): IN_MAX=3, OUT_MAX=2, WDR_MAX=1.
Public signals:
anchorRoot_pub
nullifiers_pub[IN_MAX]
out_commit_pub[OUT_MAX]
wdr_to_pub[1], wdr_values_pub[1]
fee_pub, fee_recipient_pub
assetId_pub
Checks:
Each active input’s commitment recomputed from (value, owner, blind, assetId) and its Merkle path; all inputs must bind to the same anchorRoot_pub.
Output commitments recomputed and pad‑rules enforced.
Value conservation in shares: sum(inputs) == sum(outputs) + withdrawals + fee.
Nullifiers recomputed and exposed as public signals (replay‑proof).
No deposits in a spend—deposit arrays removed from v2.
Asset binding: assetId_pub is public and must match all in/out commitments; the pool enforces that any withdrawal/fee is in that same asset.
Last updated