# Threat model

## Asset at risk

* Pool TVL: per-denomination pool balance (TON or jetton).
* User privacy: the link between depositor and recipient addresses.

## Trust assumptions

| Component                | Assumption                                                                                 |
| ------------------------ | ------------------------------------------------------------------------------------------ |
| BLS12-381 / Groth16      | Pairing is hard; soundness holds under generic group + random oracle                       |
| Trusted setup            | At least one phase-2 contributor was honest and discarded their toxic waste                |
| Poseidon                 | Collision-resistant, preimage-resistant over BLS12-381 scalar field                        |
| TVM BLS opcodes          | Implemented correctly by all TON validator software (this is a chain-wide assumption)      |
| Toncenter / lite-clients | Honest about transaction history (we cross-check root against on-chain `get_root_history`) |
| User device              | Generates good randomness; doesn't leak the note                                           |

## Attacker classes

### A1: On-chain attacker (no privileged access)

Can submit any TON transaction. Threats:

| Threat                                                          | Mitigation                                                                         |
| --------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Forge a withdraw proof                                          | Groth16 soundness (depends on trusted setup)                                       |
| Double-spend a note                                             | On-chain `nullifierHashes` dict prevents reuse                                     |
| Replay a deposit                                                | Commitments dedup'd in the same dict (top-bit marker)                              |
| Submit a withdraw to a *different* recipient than user intended | Recipient is bound to the proof as a public input; tampering invalidates the proof |
| Front-run a withdraw to claim relayer fee                       | Relayer is also bound to the proof; can't substitute                               |
| Underpay deposit (e.g. send less than denomination)             | `ERR_WRONG_DENOMINATION`                                                           |
| DoS via too-small leaf-index gas                                | TVM enforces gas limit; deposit pays its own gas                                   |

### A2: Malicious relayer

A relayer the user submits a withdrawal to. **The relayer never sees the user's note**, all proof generation is client-side. The relayer receives only the broadcast-ready payload (proof, publicSignals, recipient, fee).

| Threat                                                      | Mitigation                                                                                                                                         |
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Steal the deposit by generating its own proof from the note | **Impossible: the relayer never sees the note.** Proof is generated client-side.                                                                   |
| Tamper with recipient in the on-chain message               | Recipient is a public input bound to the Groth16 proof; tampering invalidates it (on-chain verifier rejects)                                       |
| Tamper with fee                                             | Fee bound to the proof in the same way                                                                                                             |
| Substitute itself as the relayer in someone else's proof    | Relayer is also bound to the proof; cross-check on submission catches the mismatch before broadcasting                                             |
| Link the depositor and the recipient (privacy attack)       | Client can build the Merkle tree fully locally via `GET /tree/commitments`, so the relayer never sees which specific commitment is being withdrawn |
| Censor the user (refuse to submit)                          | User can switch relayers or self-relay via `tonado withdraw self`                                                                                  |
| Front-run with own proof to claim the fee                   | Relayer is bound to the proof, only the bound relayer's address can collect the fee                                                                |
| Deanonymize via timing correlation                          | Multiple relayers + randomized delays. Out of scope for v1 mitigations.                                                                            |

### A3: Malicious depositor

| Threat                                  | Mitigation                                                                                             |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Insert garbage commitment (no preimage) | Allowed, they just lose their deposit                                                                  |
| Fill the tree to deny others            | Costs `denomination * 2^20 = $$$$$`. Economically infeasible. The tree has a fixed capacity by design. |

### A4: Counterfeit jetton attack (jetton pool only)

Critical TON-specific attack: a user deploys a contract masquerading as the pool's jetton wallet and sends `OP_JETTON_TRANSFER_NOTIFICATION` directly to the pool with arbitrary `amount` and `commitment` values. If the pool trusts the sender it has been minted free deposits.

**Mitigation:** `pool_jetton.tolk` validates `in_msg.src == state.pool_jetton_wallet` on every `transfer_notification`. The pool's jetton wallet address is computed at deploy time (via `jetton_master.get_wallet_address(pool_address)`) and baked into `ext_info`.

This is the highest-priority fuzz target in the test suite.

### A5: Validator / chain attacker

Out of scope. If TVM's BLS opcodes are buggy or validators collude, the entire chain is compromised; this isn't unique to TONado Cash.

## Owner / governance model

### Mainnet stance: burn-key

Mainnet pools are deployed with `owner = address_none()` (or a known-burned address with no recoverable private key). This makes the admin handlers **permanently uncallable** because no internal message can ever have `senderAddress == address_none()`.

What this removes:

* `OP_ADMIN_PAUSE` / `OP_ADMIN_UNPAUSE`: no kill-switch. A discovered bug cannot be paused; the only mitigations are off-chain (warn users not to deposit, advise them to withdraw if safe).
* `OP_ADMIN_RECOVER_STUCK`: no way to sweep excess TON (e.g., accidental donations, post-cycle accrual). With H-5 fixed the per-cycle accrual is bounded (\~REFUND\_RESERVE = 0.01 TON per tx), so this is not a meaningful fund-locking concern; the pool's storage rent is also funded by REFUND\_RESERVE accrual indefinitely.

What this preserves:

* **Credible neutrality.** No party can pause withdrawals, redirect funds, or otherwise interfere with users post-deploy. Matches Tornado-EVM's eventual end state.
* **No governance attack surface.** No multisig key compromise risk; no signer-coercion risk; no governance-vote-capture risk.
* **No upgrade path.** A bug requires a fresh pool deploy at a new address with the patched code; users must migrate manually. This is acceptable because the contract is small, audited, and the attack surface is bounded.

### Contract handlers under burn-key

The admin handlers are *not deleted* from the contract, that would require a separate audit pass on the changed bytecode. Instead, the `assert (in.senderAddress == storage.owner) throw ERR_NOT_OWNER` check at the top of each admin arm cannot succeed when `storage.owner = address_none()`. The handlers exist as dead code paths, unreachable in production.

### Deploy mechanism

[deploy-pool.ts](https://github.com/tonadocash/monorepo/blob/main/apps/contracts/scripts/deploy-pool.ts) accepts an explicit `--burn-owner` flag (or environment variable `TONADO_BURN_OWNER=1`) that overrides the deployer wallet address with `address_none()` in the initial state cell. The mainnet deploy script ([shell/mainnet.sh](https://github.com/tonadocash/monorepo/blob/main/shell/mainnet.sh)) is expected to pass this flag; testnet runs continue to deploy with a deployer-owned pool for iteration speed.

### Operational consequences

* **No emergency pause.** Incident response is documentation-only: post a notice on the relayer + project channels advising users to stop depositing. Existing depositors with valid notes can still withdraw through the contract (the SNARK + nullifier dict still work).
* **Storage rent is self-sustaining.** REFUND\_RESERVE accrual at \~0.005 TON net per cycle (after action fees) outpaces TON's storage rent at any realistic activity level. The pool will not run out of TON for storage. If activity halts entirely, the pool's residual balance covers years of rent.
* **No recovery of operator mistakes.** If someone accidentally sends TON to the pool address with an unrecognized op-code, the pool's empty-body short-circuit retains the funds as part of REFUND\_RESERVE accrual. They cannot be recovered by anyone.

## Privacy model

TONado Cash provides **anonymity-set privacy**: a withdraw can only be linked to one of the deposits in the same `(asset, denomination, network)` pool. The strength of the privacy depends on the size of that set.

**Observed leaks that can degrade anonymity:**

* Timing correlation between deposit and withdraw (use delayed withdraw).
* Reused recipient addresses (don't withdraw to a wallet that's been linked to your deposit-side activity).
* Always-same gas-budget patterns (small effect on TON; on EVM this was a significant fingerprint).
* Withdraw via your own depositing wallet (`tonado withdraw self` defeats privacy entirely; only use for tests).

## Audit scope summary

See [audit-scope.md](/operations/audit-scope.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tonadocash.com/protocol-reference/threat-model.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
