Skip to content

Private-money architecture

The privacy overview describes the goal. This page describes the machinery: how a balance becomes private, how a payment proves itself, and the contracts that enforce it.

Encrypted notes, not a public balance

A transparent token is a public map of address -> amount. AURA's private layer replaces that with encrypted notes. A note is a commitment to an amount and an owner; only the owner can recognise which notes are theirs, and the public sees only commitments.

Concept What it is What the public sees
Note A committed (amount, owner, salt) A commitment hash in a tree
Commitment tree A Merkle tree of all notes The root, not the contents
Nullifier A one-time marker published when a note is spent That a note was spent, not which
Viewing key Lets a chosen party detect and read notes Nothing, unless you share it

A private transfer, step by step

Spending does not reveal the note. It publishes a zero-knowledge proof that the spend is valid, plus new output notes and the nullifiers of the inputs.

flowchart LR
    IN["Input notes<br/>(owned, in the tree)"] --> PROOF{"ZK proof"}
    PROOF --> OUT["Output notes<br/>payee + change"]
    PROOF --> NUL["Nullifiers<br/>inputs now spent"]
    PROOF --> ROOT["New tree root"]
    OUT --> CH["Public chain verifies the proof,<br/>learns no amount, owner or link"]
    NUL --> CH
    ROOT --> CH

The proof establishes, in zero knowledge, that the spender owns the inputs, that the inputs are unspent (their nullifiers are new), that inputs equal outputs plus fee, and that each output commits to a valid owner. The chain checks the mathematics and updates the tree. It never learns who paid whom, or how much.

Confirm by reading state back, never by the receipt

As everywhere in AURA, a payment is "cleared" only when the state actually changed: the nullifier is recorded and the output note is in the tree. A bundle can succeed while an inner operation reverts, so the client verifies the post-state rather than trusting a receipt.

The privacy contracts

AURA keeps the private layer modular rather than one monolith, so each piece can be audited and replaced on its own.

Contract Responsibility
AuraPrivateVault Shield and unshield, the note set, nullifier set, asset registry
AuraPrivateTransfer Verify the transfer proof, conserve value, prevent double-spends
AuraViewingKeyRegistry Register and scope viewing keys for selective disclosure
AuraComplianceVerifier Check the ZK compliance proof a transfer carries

On the first production rail these are expressed against an existing shielded-asset protocol (Starknet's STRK20) through an adapter, rather than reimplemented from scratch. See Rails and chain strategy.

The invariants a private pool must hold

These become the fuzz and invariant test suite before any real value moves:

  1. Value is conserved. The sum of valid inputs equals the sum of valid outputs plus fees.
  2. Every nullifier is spent once. A replayed nullifier is rejected; this is what prevents double-spending a note.
  3. Every output has a valid owner commitment. No note can be created that no one can later prove they own.
  4. No invalid proof updates state. A transfer that does not verify leaves the tree untouched.
  5. Shield in equals unshield out. Over the lifetime of the pool, assets shielded and assets unshielded reconcile against the public token balance the vault holds.

Status

Planned The note model, the vault and transfer contracts, and the invariant suite are designed and under evaluation against Starknet/STRK20; none of this runs in the live demo yet, which remains transparent on Base Sepolia. See Implementation status.