# Threat model (https://docs.zpredict.site/docs/threat-model)

Funds are 2-of-3, order privacy is 3-of-3. What each observer and each coalition of relayers can do, and the attacks that remain.

From [DESIGN.md "Threat model"](https://github.com/zpredict/zpredict/blob/main/DESIGN.md#threat-model).

| Observer | Learns | Doesn't learn |
|---|---|---|
| Chain observer | The escrow address exists and makes batched shielded transactions | Who bet, which side, how much, who won |
| Any single relayer | "A 5-unit deposit arrived for account X"; "someone withdrew A to address Y" | Orders, sides, prices, positions, balances, whose withdrawal it was; any account's tag, so it can't trade, withdraw or read a balance as anyone |
| 2 colluding relayers | Same as one for orders; **can move escrow funds** (FROST 2-of-3) | Orders (polytune needs all 3 to collude); tags (the third share is missing) |
| All 3 colluding | Everything | — |
| Counterparty | That a fill happened at the midpoint | Who you are, your size beyond the fill |
| Network observer | That someone talks to a zPredict relayer | Contents (TLS); your IP if you use Tor |
| Cloudflare (as deployed, until #88) | Everything the three relayers receive, in clear, plus client IPs: in effect all three colluding | — once the relayer hostnames are DNS-only |
| Demo VM operator | Everything: all three relayers and the DKG ran on one machine | — with three independent operators |

**Stated up front:** funds are 2-of-3, order privacy is 3-of-3. The demo concentrates all three relayers on one VM behind Cloudflare.

## MPC security
polytune runs WRK17 authenticated garbling with distributed preprocessing (no trusted dealer). An honest relayer's shares stay private even if the other two collude and cheat; a cheater can only make the run abort, which is retried next window. Peer links are Noise `KK` with pinned static keys.

## Accounts: a credential no relayer sees
A relayer knows each account's public id and its own share of the tag, never the tag ([#85](https://github.com/zpredict/zpredict/issues/85)). Orders and withdrawals carry the tag only as fresh XOR shares, and the circuit uses a row only for a request whose shares rebuild that row's key. So a relayer posing as a browser with what it knows gets a dummy order or a refused withdrawal: test `a_relayer_cannot_act_as_an_account` in [`differential.rs`](https://github.com/zpredict/zpredict/blob/main/packages/circuit/tests/differential.rs), and the forged withdrawals in [`relayer-e2e.sh`](https://github.com/zpredict/zpredict/blob/main/scripts/relayer-e2e.sh). Balance reads are gated the same way: relayer `i` serves a balance share only to a caller holding `sᵢ`.

**Key burn.** A dishonest relayer could try to *choose* the other relayers' shares for an id it learned, by sending a judge claim or a deposit memo of its own for that id first. Honest shares are deterministic in the tag, so the victim's later deposit shows a different share for a known id, and that relayer then **burns** the key (fresh random share every batch). The row is lost, never handed over: the same denial of service a relayer can always cause by aborting the MPC.

## Signers check what they sign
Before a FROST signature on a payout, each signer recomputes the sighash from the PCZT and checks every output against the agreed payout vector (`verify_then_sign`). A coordinator that changes one output by one zatoshi gets a refusal. See [Settlement & FROST](https://docs.zpredict.site/docs/settlement).

## Not quantum-recoverable (ZIP 2005)
The escrow's spend authority `ak` is the FROST group key, so its viewing key cannot be derived from a seed; `nk` and `rivk` are random, generated once at the DKG. Escrow notes therefore don't use the ZIP 2005 derivation that would let a key holder recover them after a quantum break of discrete log. Acceptable for a hackathon escrow that holds collateral for days; a production escrow needs a FROST-compatible ZIP 2005 derivation, or a sweep to a recoverable key first.

## Open issues
| Issue | Effect |
|---|---|
| [#88](https://github.com/zpredict/zpredict/issues/88) | Cloudflare terminates TLS for all three relayers |
| [#89](https://github.com/zpredict/zpredict/issues/89) | Cloudflare injects an analytics beacon (CSP blocks it) and NEL headers |
| [#90](https://github.com/zpredict/zpredict/issues/90) | Connection-level metadata can link orders and withdrawals to an account id |
| [#91](https://github.com/zpredict/zpredict/issues/91) | Withdrawal amount and timing can link deposits to payouts at the relayers |
