zPredictdocs
App
Menu

Threat model

View .md

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

ObserverLearnsDoesn't learn
Chain observerThe escrow address exists and makes batched shielded transactionsWho 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 relayersSame 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 colludingEverything—
CounterpartyThat a fill happened at the midpointWho you are, your size beyond the fill
Network observerThat someone talks to a zPredict relayerContents (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 operatorEverything: 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). 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, and the forged withdrawals in 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.

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

IssueEffect
#88Cloudflare terminates TLS for all three relayers
#89Cloudflare injects an analytics beacon (CSP blocks it) and NEL headers
#90Connection-level metadata can link orders and withdrawals to an account id
#91Withdrawal amount and timing can link deposits to payouts at the relayers