# Stealth Addresses
Transacting on the MWEB happens via stealth addresses. Stealth addresses are supported using the *Dual-Key Stealth Address Protocol (DKSAP)*.
These addresses consist of 2 secp256k1 public keys (Ai, Bi) which are derived from 2 master keypairs (a, A) and (b, B),
and which can be used repeatedly to generate new outputs that are unlinkable by those without knowledge of the master keys.
### Notation
* G, H, and J are curve generator points
* Uppercase letter in code font (A, Ai, etc.) represents a secp256k1 public key
* Lowercase letter in code font (a, ai, etc.) represents a scalar
* A letter in single quotes ('K') represents the ASCII value of the character literal
* HASH32(x||y||z) represents the standard 32-byte `BLAKE3` hash of the conjoined serializations of `x`, `y`, and `z`
* HASH32(...) outputs are interpreted as scalars modulo the curve order where scalar arithmetic is used.
* COMMIT(v,x) = v⋅H + x⋅G represents the pedersen commitment generated with value v and blinding factor x.
* BLIND_SWITCH(v,r) = r + HASH32(r⋅G + v⋅H || r⋅J) represents the blinding factor for a value v and randomness r.
* x[y,z] suffix represents the byte range from y to z (inclusive, zero-based indices) of the input x
### Address Generation
Unique stealth addresses should be generated deterministically from a master scan keypair (a, A) and master spend keypair (b, B) using the following process:
1. Choose the lowest unused address index i
2. Calculate one-time spend keypair (bi, Bi) as:
bi = b + HASH32('A'||i||a)
Bi = bi⋅G
3. Calculate one-time scan keypair (ai, Ai) as:
ai = a⋅bi
Ai = ai⋅G
### Outputs
Outputs consist of the following data:
* Co - The pedersen commitment to the value.
* Ks - The sender's public key.
* Ko - The receiver's one-time public key.
* Output message, m, consisting of:
* Feature bits
* Ke - The key exchange public key.
* t - The view tag.
* v' - The masked value.
* n' - The masked nonce.
* Optional extra data (if enabled by feature bits)
* A signature using the sender's key ks.
* A rangeproof of Co that also commits to the m.
### Output Construction
To create an output for value v to a receiver's stealth address (Ai,Bi):
1. Generate a random sender keypair (ks, Ks).
2. Derive the nonce n = HASH32('N'||ks)[0,15]
3. Derive the sending key s = HASH32('S'||Ai||Bi||v||n)
4. Calculate P = s⋅Ai. Derive the view tag and shared secret as:
t = HASH32('T'||P)[0]
e = HASH32('D'||P)
5. Calculate the receiver's one-time public key Ko = HASH32('O'||e)⋅Bi
6. Calculate the key exchange pubkey Ke = s⋅Bi
7. Encrypt the value using v' = v ⊕ LE64(HASH32('Y'||e))
8. Encrypt the nonce using n' = n ⊕ HASH32('X'||e)[0,15]
9. Calculate the blinding factor x = BLIND_SWITCH(v,HASH32('B'||e))
10. Calculate the commitment Co = COMMIT(v,x)
11. Generate the rangeproof π for Co, committing also to the output message m. TODO: Link to 'm' definition and rangeproof spec
12. Sign the message HASH32(Co||Ks||Ko||HASH32(m)||HASH32(π)) using the sender key ks.
### Output Identification
To check if an output belongs to a wallet:
1. Calculate the ECDHE point P = a⋅Ke.
2. If HASH32('T'||P)[0] does not match the view tag t, the output does not belong to the wallet.
3. Derive the shared secret e = HASH32('D'||P).
4. Recover the one-time spend pubkey: Bi = Ko / HASH32('O'||e).
5. Lookup the index i that generates Bi from the wallet's map Bi->i. If not found, the output does not belong to the wallet.
6. Decrypt the value v = v' ⊕ LE64(HASH32('Y'||e)).
7. Decrypt the nonce n = n' ⊕ HASH32('X'||e)[0,15].
8. Verify that COMMIT(v, BLIND_SWITCH(v,HASH32('B'||e))) =? Co.
9. Calculate the send key s = HASH32('S'||Ai||Bi||v||n).
10. Verify that Ke =? s⋅Bi.
If all verifications succeed, the output belongs to the wallet, and is safe to use.
#### Spend Key Recovery
The spend key can be recovered by ko = bi⋅HASH32('O'||e).
Derivation proof:
ko = bi⋅HASH32('O'||e)
Ko = HASH32('O'||e)⋅Bi
= HASH32('O'||e)⋅(bi⋅G)
= (bi⋅HASH32('O'||e))⋅G
= ko⋅G
### Implementation Notes
* Litecoin Core MWEB uses tagged BLAKE3 hashes with one-byte domains ('A', 'B', 'D', 'N', 'O', 'S', 'T', 'X', 'Y') and a multiplicative output-key tweak (Ko = Bi⋅HASH32('O'||e)).
* This differs from some DKSAP writeups that use additive output-key tweaks or define the view tag directly as the first byte of e.
* Wallets must keep a map Bi->i of all used spend pubkeys and the next several unused ones.
* To be consistent with Litecoin Core, we recommend using the following derivation paths:
* Master scan key (a, A): m/0'/100'/0'
* Master spend key (b, B): m/0'/100'/1'