# 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.

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](/concepts/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](/concepts/asp-attestation) 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](/concepts/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](/protocol/circuits/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`
