What is hardened, what is trusted, and the roadmap to mainnet.
- Stealth layer (graph privacy). Pure Ed25519 ECDH, no ZK. A payer funds a
one-time address; only the holder of the scan key can detect it and only the
holder of the spend key can move it. The announcer stores
R+ a view tag, both public by construction; they reveal nothing without the scan key. Stealth covers the payer-recipient link; the default path then routes through the relayer and the pool so the funding and fee trail is closed too. - Pool layer (value privacy). Tornado/Semaphore-style: deposit a fixed
denomination against
precommit = Poseidon(nullifier, secret); withdraw with a Groth16 proof of membership, binding the recipient and a single-use nullifier. The payout comes from the vault PDA viainvoke_signed; there is no operator or authority key. Anonymity set = the count of same-denomination deposits. - Relayer. Trusted for liveness/censorship only, never for custody or privacy. It pays fees so a fresh account never originates one. It cannot move user funds (the pool pays out of the vault, not the relayer).
- Relayer cannot be drained. It refuses any transaction that is not feePayer
= relayer, references an unlisted program, references the relayer inside a
System instruction (no relayer-funded transfer/create), or whose simulated cost
to the relayer exceeds
MAX_RELAYER_COST_LAMPORTS. Plus a minimum-balance floor, per-IP and global rate limits, and tx size / instruction-count caps. Verified: a "transfer the relayer's SOL to me" request is rejected; legitimate sweep / pool-deposit / withdraw relays pass. - Indexer is untrusted. It serves only public data (announcements, pool leaves). The client rebuilds its own Merkle path locally, so the indexer never learns which leaf a recipient withdraws.
- Single-use enforcement on-chain. Gate nullifier, withdraw nullifier, and
announcement PDAs are
init(notinit_if_needed), so replays fail. - Value is bound to lamports moved. The deposit pins the leaf value to the amount actually transferred, and denominations are fixed, so same-denom deposits are indistinguishable and the amount cannot be forged.
SEAL runs on devnet today. The path to mainnet:
- Multi-party trusted-setup ceremony. The current proving keys come from an
initial setup; a multi-party ceremony with independent contributors and a
public randomness beacon (
CEREMONY.md) produces the keys baked into the mainnet program. - External review of the circuits, the Anchor programs, and the SDK.
- Anonymity set. A withdrawal's privacy scales with the same-denomination deposits in the pool; mainnet liquidity widens it.
- Operational. Burn or multisig the program upgrade authority, and rebuild the pool program for the current SBPF target on the mainnet deploy.
SEAL is on devnet. Security issues: open a private report to the maintainer before public disclosure.