CLOISTER DOCS
ENDE
Website Open App →

Cloister — Security

Threat model and the controls that address each threat, as built. Validation evidence is in VALIDATION.md.

Assets & adversaries

Contract controls (ShieldedPool.sol)

Threat Control
Reentrancy via hook tokens (ERC-777/1363) ReentrancyGuard + strict Checks-Effects-Interactions: all state (nullifiers, root, index) is written before any token transfer
Fee-on-transfer / rebasing under-collateralisation deposit credits only the measured balance delta; a short transfer reverts (fee-on-transfer unsupported)
Non-standard ERC-20 (USDT-style no-return) SafeERC20 for every transfer
In-tx double-spend inputNullifiers[0] != [1] (and the circuit also asserts it)
Cross-tx / cross-lane double-spend global nullifierSpent set
Stale / forked root oldRoot == laneRoot[lane]
Lane overflow explicit laneNextIndex + 2 <= 2^levels ("lane full") guard
Funds frozen by operator guardian can pause deposits; a time-boxed emergencyPause can halt all tx for incident response but is non-renewable (a PAUSE_COOLDOWN guarantees an open withdrawal window between pauses) → funds can never be permanently frozen
Compliance bypass asp == 0 (dev) or knownAspRoot[associationRoot]; the circuit proves real inputs ∈ that root; the ASP can revoke a root (revokeAspRoot) if it later proves to contain illicit notes
Public-input range the gnark verifier rejects any public input ≥ p (checkField)
Forged value circuit range-checks all amounts to 248 bits + conservation in-field
Redirected withdrawal / fee recipient, relayer, fee, encrypted outputs are bound via ExtDataHash (a public input)
Registry hijack PoolRegistry is Ownable2Step, append-only register, explicit migrate emits old+new

Circuit controls

The circuit is the second line for double-spend (AssertIsDifferent) and the only line for value conservation, membership, compliance, and the off-chain insertion proof. See CIRCUIT.md for the per-constraint soundness argument (field-wrap, empty-slot, nullifier binding, extData binding).

Relayer / submission controls

Defense-in-depth

The contract re-checks invariants the circuit already guarantees (distinct nullifiers, spent-set), so even a (hypothetically) compromised verifier cannot enable an in-tx or cross-tx double-spend or drain the pool via reentrancy.

Known residual risks (must be addressed before mainnet)

  1. Trusted setup: keys come from a single groth16.Setup run. Mainnet requires a multi-party Phase-2 ceremony.
  2. ASP trust: the ASP defines the good set; a malicious ASP could include illicit commitments. This is a policy/operational control, not a cryptographic one.
  3. Registry / guardian / ASP keys: in production the owner/guardian/ASP must be a multisig + timelock.
  4. Token assumption: the pool assumes a well-behaved ERC-20 at deploy; fee-on-transfer is rejected at runtime, but the deployed token address must be the real asset.
  5. Audit: an independent external audit of contracts + circuit is required before handling real value. The findings here are from internal review + the soak in VALIDATION.md.
On this page
Assets & adversariesContract controls (ShieldedPool.sol)Circuit controlsRelayer / submission controlsDefense-in-depthKnown residual risks (must be addressed before mainnet)