Stealth Addresses: The Math
One-time output keys via ECDH: R = rG, P = Hs(rA)G + B, how the receiver recovers the one-time private key, subaddresses, and view tags.
Your public address can be posted on a billboard, yet no payment to it ever appears on the blockchain. That isn't hand-waving — it's a precise ECDH (elliptic-curve Diffie–Hellman) construction that mints a fresh one-time output key for every payment. Here's the actual math, assuming the curve and keys from the last lesson.
The Sender's Side
You publish the public view/spend keys (A, B). To pay you, the sender:
- Generates a random transaction private key
r(a scalar) and publishesR = rG— the tx public key, stored intx_extra. - Computes the shared secret for output index
i:Hs(r·A ‖ i). Noter·A = r·(aG) = a·(rG) = a·R— the ECDH trick: both parties can compute it, nobody else can. - Sets the one-time output public key:
P = Hs(r·A ‖ i)·G + B
P is what actually goes on-chain as the output's destination. It's unique to this payment, looks random, and can't be linked back to B by an observer.
The Receiver's Side
You scan each output's P and the tx's R using your private view key a:
P' = Hs(a·R ‖ i)·G + B
Because a·R = r·A, if P' == P the output is yours. You found it with only the view key — no spend key needed (this is exactly what a view-only wallet does). To spend it, you need the one-time private key, which requires your private spend key b:
x = Hs(a·R ‖ i) + b and indeed P = x·G
So you (and only you) hold the discrete log x of the on-chain key P. Note the view key alone can't compute x (it lacks b) — detection and spending are cleanly separated.
Subaddresses Change the Recipe
Subaddresses (the 8… addresses) let you hand out unlimited unlinkable addresses from one account. A subaddress for index (account=m, index=n) derives a per-subaddress spend key:
D = B + Hs(a ‖ m ‖ n)·G (public spend)
C = a·D (public view)
The sender now computes R as r·D (not rG) so the math still closes, and the wallet recognizes outputs by precomputing a table of its subaddress spend keys. The upshot: subaddresses are unlinkable to each other and to the base address, with no extra on-chain cost.
View Tags: the 2022 Speedup
Scanning used to require the full Hs(a·R ‖ i)·G + B computation for every output on the chain — a lot of scalar mults. Since the v15 upgrade (Aug 2022) each output also carries a 1-byte view tag:
view_tag = first byte of Hs("view_tag" ‖ a·R ‖ i)
The wallet computes the cheap shared secret, checks the one byte first, and discards ~99.6% of non-matching outputs before doing the expensive point arithmetic — cutting wallet sync time dramatically. It leaks essentially nothing (1 byte collides for 1/256 of outputs by design).
So your address is permanent and public, yet every payment lands on a fresh, unlinkable one-time key only you can spend. Next: hiding which output you spend, with CLSAG Ring Signatures & Key Images.
Comments
Log in or create a free account to comment.
No comments yet — be the first.