Skip to main content

Ragequit circuit

The ragequit circuit is the public-exit proof for any unspent note. The current note owner proves keystore membership and knowledge of the note secrets, and recovers the funds to their owner address, sacrificing privacy on that specific note in exchange. depositSecret is not an input here. That secret lives only in the deposit circuit.

Public signals

  • keystoreRoot: the root of the keystore tree used to verify the owner is registered.
  • ownerAddress: the note owner, who is also the forced withdrawal recipient.
  • value, tokenId: what's being recovered.
  • label: the lineage identifier. It matches the deposit's label, which lets the ASP link the ragequit back to its deposit.
  • Public outputs: nullifierHash (consumed by the contract to mark the note spent) and commitment (the recomputed commitment the contract verifies against its commitments mapping).

Private inputs

  • noteSecret: the secret used to reconstruct the commitment.
  • metadata: fixed to 0 in v2.0.
  • privateNullifyingKey, privateRevocableKey: used to prove the owner's keystore leaf.
  • keystoreLeafIndex, keystoreSiblings, keystoreTreeDepth: the keystore Merkle inclusion witness.

Constraints

The circuit first proves keystore inclusion. It hashes the nullifying key into nullifyingKeyHash = Poseidon(privateNullifyingKey), computes authDigest = Poseidon(0, privateRevocableKey), and checks that the leaf Poseidon(ownerAddress, nullifyingKeyHash, authDigest) sits on a Merkle path landing on keystoreRoot.

It then recomputes the commitment from (Poseidon(ownerAddress, noteSecret), tokenId, value, metadata, label). The contract checks that the recomputed commitment exists in its commitments mapping (via _commitmentExists, since the state tree stores timestamped hashes rather than raw commitments).

No ASP root membership is required, because bypassing the ASP is the whole point of this circuit. The contract additionally enforces msg.sender == ownerAddress, so the caller cannot redirect the funds to a different address.

Tree depth

The circuit signature is Ragequit(18), where the 18 bounds the keystore Merkle proof depth. Unlike transact, the ragequit circuit does not contain a state-tree membership proof. The contract verifies commitment existence by direct lookup rather than by Merkle proof.

Privacy implication

The on-chain Ragequit event publishes the recipient (ragequitter), the asset, the value, the spent commitment, the nullifierHash, and the label. Because the Deposited event publishes the note's outputCommitment, ragequitting a deposit note links it back to the originating deposit from chain data alone. No off-chain mapping from label to deposit is needed to close that link. Use it only when the note is otherwise unspendable.

Source: v2-monorepo/packages/circuits/circuits/main/ragequit.circom