System Overview
The architectural design enforces that the value lives in encrypted notes, spending is authorized by deriving nullifiers, and balance is enforced by value commitments and a binding signature. Platus adapts that model to the EVM, to the BabyJubJub curve and Poseidon2 hash, and to an account-abstraction execution layer that lets shielded value drive arbitrary on-chain calls.
The core invariant: the chain learns almost nothing
A Platus transaction (Orchestrator.transact) exposes only:
- a set of nullifiers being marked spent (opaque field elements),
- a set of note commitments being inserted into the tree (opaque field elements),
- per user, a net public value and a single asset — how many tokens cross the pool boundary, if any,
- two ciphertext blobs per Action that only the recipient (or the sender) can open.
Nothing on-chain links a spent note to a created note, reveals a note's value, or reveals a recipient. A deposit, an internal transfer, and a withdrawal all present the same structural shape. The unlinkability rests on a ZK proof that the prover knows secret inputs satisfying the circuit — without revealing them.
The unit of work: the 2-in / 2-out Action
Every private transaction decomposes into one or more Actions. A single Action:
- spends up to two input notes (
in1,in2) under one owner key triple(ak, nk, rivk), - creates up to two output notes (
out1,out2), - carries one net value commitment
cv_net, - emits exactly one Poseidon2 digest as its public output.
Per-side enable_spend_* / enable_output_* flags let any side be a dummy, so the same fixed circuit shape serves the modal case ("1 input → payment + change") and consolidation ("2 inputs → 1 output + dummy") alike.
The only on-chain artifacts of an Action are the two nullifiers and the two commitments. Their relationship to the private (value, address, asset, randomness) is provably correct but perfectly hiding.
End-to-end flow
A private payment crosses every layer exactly once:
Three properties make this safe and cheap:
- The user proves; the bundler aggregates. Private witnesses never leave the device. The bundler only ever sees opaque public inputs and finished proofs.
- On-chain verification is constant-cost. The recursive aggregation collapses up to 18 Actions into a single
finalizeproof, verified in roughly constant gas regardless of batch size. - Spend authority is proved, not relayed. Spend-authorization is verified inside the circuit against the bundle's context, so the contract never runs a per-Action signature check.
For the full step-by-step, see Transaction Lifecycle.
Trust boundaries
| Property | Enforced by | An attacker who controls… cannot… |
|---|---|---|
| Value conservation | binding signature + on-chain reconstruction | the bundler · mint or burn value |
| Spend authority | in-circuit Schnorr over rk | the bundler · spend your notes |
| No double-spend | persistent + transient nullifier set | anyone · replay a note |
| Recipient privacy | stealth addresses + hybrid encryption | the indexer · learn who received |
| Correct execution | the immutable Orchestrator | anyone · re-route settled funds |
The bundler is a liveness dependency, not a safety dependency. If it is offline or hostile, a user can self-aggregate and submit transact themselves; the worst a bundler can do is refuse service or grief (e.g. tamper with a hint the proof does not bind — see the security model).