PARANOID

How it works

1. Deposit

deposit(commitment, epochs) pays rate USDC for each of epochs epochs starting with the current one (between 1 and max_epochs). The program inserts the leaf Poseidon(commitment, end_epoch) into the tree and records the member at ["member", commitment].

Your wallet signs the deposit, so the chain links that wallet to the commitment. It cannot link the commitment to any request: requests prove membership of the whole tree without saying which leaf.

2. Request

For each request the client proves, in zero knowledge:

  • it knows a secret whose leaf Poseidon(Poseidon(secret), endEpoch) is in the tree under a public root;
  • the request's epoch is not after endEpoch (the member is paid up);
  • it spends weight slots (1 to 4) from a private window [start, start + weight) inside the epoch's limit.

For every slot id it spends, the proof outputs one point of a line and one nullifier:

a1        = Poseidon(secret, Poseidon(epoch, APP), id)
y         = secret + a1 * x          (x is the hash of this request)
nullifier = Poseidon(a1)

APP is the constant "paranoid.v1". The gateway keeps the nullifiers of the current epoch. A new nullifier means a fresh slot; a repeated nullifier means the same slot was used again.

3. Cheating gives you away

One point of a line says nothing about where the line crosses zero. Two different points of the same line fix it:

a1     = (y2 - y1) / (x2 - x1)
secret = y1 - a1 * x1

So a member who spends the same slot on two different requests hands its secret to the gateway. Anyone holding the secret can call slash(secret, siblings): the program checks Poseidon(secret) == member.commitment, pays out the epochs already reached, and forfeits every unpaid epoch, slash_burn_bps of it to the burn vault and the rest to the treasury. The reporter receives nothing, so there is nothing to front-run.

4. Billing by time

accrue (anyone may call it) pays the treasury and the burn vault for every epoch a member has reached. Billing never depends on how many requests were made, so the chain learns nothing about usage.

5. Leaving

request_exit stops the membership after the current epoch and takes the leaf out of the tree. Roots that still contain it stay valid only through this already paid epoch. After exit_delay full epochs, the slashing window, withdraw refunds every unpaid epoch and closes the member account.

Next: Security and trust