One-time addresses
Stealth addresses: sending money to an address without writing it on the blockchain
The sender picks a random r, publishes R = r·G and sends money to the one-time key P = Hs(8·r·A ‖ i)·G + B. The recipient finds it by computing the same thing via a: Hs(8·a·R ‖ i)·G + B. They spend it with the private key x = Hs(8·a·R ‖ i) + b.
The task
We want:
- neither the recipient's address nor anything that repeats across payments to one person to appear on the blockchain;
- the recipient to find their outputs themselves, without talking to the sender;
- only the recipient to be able to spend the output.
The solution is a Diffie–Hellman key exchange built into every transaction.
Step by step
1. The sender picks a random r
A fresh random r (transaction private key) for every transaction. Its public version R = r·G (transaction public key) is written into the transaction's tx_extra field.
2. The shared secret
The sender computes 8·r·A (A is the public view key from the address). Later the recipient computes 8·a·R. It's the same point, because a·R = a·r·G = r·a·G = r·A. In Monero's code it's called the key derivation.
The factor 8 (the cofactor) "cleans" the point of any small-order component: even if someone supplies a malicious R, the result lands in the correct subgroup.
3. The one-time key
This is the one-time address (output public key). It looks like a random point: without a or r there's nothing to compare it with.
4. The recipient scans
For every new transaction and every output i the wallet computes Hs(8·a·R ‖ i)·G + B and compares it with P. Match — the output is ours. This needs only the view key a and the public B — so a view-only wallet can find incoming payments.
5. The recipient spends
The one-time output's private key:
This needs the spend key b. The key x signs the spend, and the key image is computed from it.
The amount is encrypted with the shared secret too
A mask for the amount is derived from the same shared secret. The output amount (8 bytes) is stored as amount ⊕ Keccak("amount" ‖ Hs(8rA ‖ i))[0..8], and the commitment's blinding factor as Hs("commitment_mask" ‖ Hs(8rA ‖ i)). The recipient computes the same and decrypts the amount. More in Pedersen commitments.
Weak spots
- Scanning is expensive: a point multiplication for every output of every transaction. Partly addressed by view tags.
- The Janus attack: a sender who suspects two subaddresses belong to one person can build an output from parts of both and see whether the recipient "notices" the payment. Wallets have partial protection; Carrot closes it fully.
- The sender knows r and can prove to a third party that they paid a particular address (sometimes useful — a "payment proof").