BIP 352 · Status: Complete · Version 1.1.1
How a payment finds you
without anyone announcing it
Silent payments solve one problem: give somebody a single address they can keep, and still make every payment land somewhere new. No message to you, no notification transaction, no extra bytes on the chain, and no way for two people who paid you to work out they paid the same person.
The trick is one sentence long. The sender’s own transaction inputs already contain a public key, and it is already on the chain. That is enough for both sides to agree on a secret nobody else can compute. Everything below is the detail.
§ 01 · The two keys
Why a silent payment address holds two keys, not one
Finding an incoming payment requires a multiplication with a private key. If your wallet had only one key, that key would have to sit on a machine that is always online, and that same key would spend your money. So the address carries two.
b_scan
and the public B_spend can see every payment you receive and compute every
output key, and still cannot move a satoshi. Handing that scan key to a service is a
real privacy decision, not a security one.
- Address format
- bech32m, human readable part
sp, version characterq - Payload
- 66 bytes: the compressed
B_scanthen the compressedB_spend - Length
- 116 characters on mainnet, 117 on testnet and signet where the prefix is
tsp - Not in the spec
sprtfor regtest is a convention from one Rust crate, not a standard
The spec’s own footnote says “117 characters”, computed with a
three-character prefix. Mainnet uses the two-character sp, so mainnet
addresses are 116. All twelve addresses in the official test vector file are 116.
Take one apart
A real address from the test vectors, in pieces. Point at a piece to find out what it is.
The human readable part. sp on mainnet, tsp on testnet and signet. Two characters here rather than three is the entire reason a mainnet address is 116 characters and the specification’s own footnote says 117.
The separator. Always the character 1, which is why it is the one character bech32 does not use anywhere else. Everything to its left is the prefix, everything to its right is data.
The version. q is zero. A sender meeting versions 1 to 30 must read the first 66 bytes and ignore the rest, so the format can grow. Version 31 is reserved for a change that deliberately breaks old senders.
B_scan, the public scan key. 33 bytes, the half that finds payments. Anyone reading your address holds this, and it is useless to them: finding a payment needs the matching private key, which never leaves your wallet.
B_spend, the public spend key. 33 bytes, the half every output key is built on. A note on honesty: 33 bytes is 52.8 bech32 characters, so the true boundary between the two keys falls inside a character. This split is at the nearest one.
The checksum. Six bech32m characters. Mistype one character anywhere in the address and this will not match, so your wallet refuses rather than sending money to nobody.
§ 02 · The derivation
The whole thing, one step at a time
Every number printed in this diagram is real. They are the values from test case 0 of the official BIP 352 test vectors, recomputed from scratch and checked against the file. If you implement this and get these numbers, you got it right.
Alice picks her coins
Only four kinds of input can contribute to the secret. Anything with branches or several keys, like a multisig script, is excluded, because a hostile co-signer could re-sign with a different key set and the payment would land somewhere Bob never looks. Excluded inputs can still pay the fee.
She adds her private keys together
One number, not one per input. That single change, made in October 2022, is what halved the receiver’s work and opened the door to collaborative transactions. Taproot keys are x-only, so both sides have to agree on the sign.
She picks the smallest outpoint
Lexicographically smallest across every input, serialised the way it appears in the raw transaction, which is byte reversed from the txid you read on an explorer. Using one fixed outpoint makes the result independent of input ordering, and keeps it computable on a hardware signer with very little memory.
She hashes both together
This is input_hash, and it is the part people skip. It makes the secret
unique to this transaction, and it commits to A so that a malicious
sender cannot pick a key that forces a collision. Take it out and the protocol still
appears to work, while quietly reintroducing address reuse.
Diffie-Hellman
Alice can compute this because a is hers and B_scan is
printed in the address she was given. Nobody watching can, because they hold neither
private half. The mathematics is from 1976 and has not needed changing.
The output key
Hash the secret with a counter, add the result to B_spend, and that is
the destination. Paying the same person twice in one transaction just increments the
counter. Every output the wallet generates must actually be broadcast: drop one and
the receiver’s scan stops there and misses everything after it.
Nothing announces itself
What reaches the chain is an ordinary taproot payment. No marker, no notification, no extra byte. This is why nobody can count silent payments, including the firms paid to count things, and why the anonymity set is every taproot payment rather than every silent payment.
The two secrets meet
Bob reads the input public keys and the outpoints from the transaction itself, so he
can compute the same input_hash, and b_scan · A gives
him the identical point Alice got from a · B_scan. That is the
whole reason there is no notification: the data was always there.
The same walk, in real numbers
Test case 0 from bip-0352/send_and_receive_test_vectors.json, two P2PKH inputs
paying one silent payment address. Every line was recomputed and matched against the file.
# two inputs in[0] txid f4184fc5…831e9e16 vout 0 a₁ = eadc7816…e5dff3b1 in[1] txid a1075db5…fbf5d48d vout 0 a₂ = 93f5ed90…9408aad16 # serialised outpoints are byte reversed. this is the one exception in the spec. in[0] → 169e1e83e930853391bc6f35f605c6754cfead57cf8387639d3b4096c54f18f4 00000000 in[1] → 8dd4f5fbd5e980fc02f35c6ce145935b11e284605bf599a13c6d415db55d07a1 00000000 outpoint_L = 169e1e83…c54f18f400000000 # in[0] is smaller # sums a = 7ed265a6dac7aba8508a32d6d6b84c7f1dbd0a0941dd01088d69e8d556345f86 A = 032562c1ab2d6bd45d7ca4d78f569999e5333dffd3ac5263924fd00d00dedc4bee # the binding hash input_hash = 5bfe5321d759e01a2ac9292f0f396ff9c3d8b58d89ccb21a6922e84bb7ad0668 # what an index server serves to a light client: input_hash · A tweak = 024ac253c216532e961988e2a8ce266a447c894c781e52ef6cee902361db960004 # the shared secret, computed independently on both sides sender : input_hash · a · B_scan = 028158aff7d61ea66b2fa7f555bc3c5937d1debbde16423d630f9aa7943e14d80d receiver: b_scan · (input_hash · A) = 028158aff7d61ea66b2fa7f555bc3c5937d1debbde16423d630f9aa7943e14d80d # the output t₀ = f438b40179a3c4262de12986c0e6cce0634007cdc79c1dcd3e20b9ebc2e7eef6 P₀ = B_spend + t₀·G P₀ x-only = 3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1 scriptPubKey = 5120 3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1 # an ordinary taproot output. that is the entire disguise.
- The three tags
BIP0352/InputsBIP0352/SharedSecretBIP0352/Label- Tagged hash
SHA256(SHA256(tag) ‖ SHA256(tag) ‖ x), the BIP 340 convention- Output type
- Always P2TR in version 0. No optionality, so everyone lands in the same anonymity set
- Signature hash
SIGHASH_ALLorDEFAULT.ANYONECANPAYis unsafe: the input set would be free to change after signing, and every derived output would move
§ 03 · The part people skip
Why input_hash is in there
Take it out and the protocol still appears to work. It also quietly reintroduces the exact problem it was written to solve.
A, not just the outpoint. Without that, a malicious
sender could solve for a private key a′ that cancels the difference and
force the collision on purpose. Committing to A = a·G inside
the hash turns that into a fixed point problem nobody can solve.
§ 04 · Labels
Many addresses, one scan
One identity can hand out addresses that look completely different, and still cost the same to scan. Add a tweak to the spend key before encoding:
Bₘ = B_spend + hash(ser_256(b_scan) ‖ m)·G
tag: BIP0352/Label
Detection costs nothing extra. The wallet computes Pₔ as usual, then
subtracts it from the output and looks the difference up in a small precomputed table.
One lookup, not one extra multiplication per label.
Label 0 is reserved for your own change, and must never be handed out. If somebody else had it they could create outputs your wallet would file as its own change. Scan for it always, even if you use no other labels, so a wallet restored in different software still finds it.
The misconception worth killing
Look at the highlighted middle of all three. That is the same encoded
B_scan, because labels only tweak the spend half. Anyone holding two of
your labelled addresses can see at a glance they are one person.
Labels sort your incoming payments. They are not separate identities. The payments themselves stay unlinkable. The published strings do not.
§ 05 · The bill
Somebody has to scan
Nothing is free. The cost of removing the notification, the round trip and the on-chain overhead is that your wallet has to look at transactions rather than being told about them. Here is the real size of that bill.
30 days
Rates are the published Frigate benchmarks: 286,404 transactions per second on the laptop, 569,123 on the desktop, 17,188,956 on the graphics card. Blocks are counted at 144 a day and 887.5 transactions each, which is what Frigate measured. Tweak data is estimated at 10 kB per block, the middle of the 7 to 12 kB the specification reports for the chain as it is today. Your phone is not on this list, and that is the honest part: the unsolved problem is not the arithmetic, it is fetching that data without telling a server which payments are yours.
| Hardware | 1 day | 1 month | 1 year | 2 years |
|---|---|---|---|---|
| M1 MacBook Pro, 10 core CPU | 2.7 s | 20.0 s | 3 m 28 s | 7 m 47 s |
| Core Ultra 9 285K, 24 core CPU | · | · | 1 m 44 s | 3 m 50 s |
| RTX 5080, CUDA | · | · | 3.5 s | 7.7 s |
| Two RTX 5090, CUDA | · | · | 2.1 s | 3.2 s |
A year of chain in three and a half minutes on a laptop is not a research problem. The research problem is the phone: a light client has to get the 33 bytes per transaction from somewhere, and getting them without telling a server which payments are yours is the unsolved part. The spec says so itself: “It is still an open question as to how Bob can source the 33 bytes per transaction in a trustless manner.”
Bandwidth today
Roughly 7 to 12 kB per block, so 30 to 50 MB a month. If every transaction on the chain became taproot it would rise toward 100 kB per block.
The cut-through trick
Half of all transactions have their taproot outputs spent within one block. A wallet scanning every three days can skip those entirely and land near 30 MB a month.
The denial of service fix
A single hostile 23,230 output transaction used to take 165 seconds to scan. Version 1.1.0, March 2026, capped recipients per group at K_max = 2323. Now 17 seconds, and no real wallet notices, because a 2,323 output transaction is already 99,959 vbytes and one more output breaks the relay limit.
§ 06 · Honesty
What silent payments do not do
An exchange cannot pay you privately
The sender needs the private keys of the coins being spent. If you withdraw from a custodian to a silent payment address, the custodian has to implement this, and almost none have. Ruben Somsen flagged it in the original 2022 proposal and it is still open: “No solution is known at this time, so as it stands this is a limitation of the protocol.”
CoinJoin is not proven safe
The spec is blunt: it is “recommended that the keys of all inputs of a transaction belong to the same entity as there is no formal proof that the protocol is secure in a collaborative setting.” Doing it properly needs blinded key exchange with discrete log equality proofs, which is why BIP 374 exists as a separate document.
There is no Lightning here
A silent payment is an on-chain taproot output derived from on-chain inputs. It has no meaning inside a payment channel. The Lightning shaped answer to the same problem is a BOLT 12 offer, and a payment name can carry both at once.
Fee bumping can break it
Change the input set and every derived output moves, because the inputs are the ingredients. A wallet that replaces a transaction without re-deriving sends money to a key nobody can find. Electrum deliberately sets the sequence number so unsafe fee bumps cannot happen.
Old software cannot pay you
The spec says it: silent payments are “not compatible with older wallet software or wallets which have not implemented the silent payments protocol.” Which is exactly why the next section is uncomfortable reading.
What it does do, completely
One address you can publish forever, a fresh unlinkable output for every payment, no interaction, no notification transaction, no extra bytes, and outputs that are indistinguishable from every other taproot payment on the chain. That is the whole promise and it is delivered.
§ 07 · Reality check
Who can actually do this
The cryptography is finished. The wallet layer is thin. The merchant layer does not exist. Anyone telling you otherwise is selling something.
| Software | Send | Receive | Since |
|---|---|---|---|
| Sparrow Wallet | yes | yes | 2.3.0 · 2025-10-03 · receive in 2.5.0 · 2026-05-21 |
| Cake Wallet | yes | yes | first merged 2024-05-31 |
| Dana Wallet | yes | yes | experimental |
| BlindBit Desktop | yes | yes | alpha |
| BitBox02 | yes | · | 2024-09-20 |
| Wasabi Wallet | yes | · | 2.7.2 · 2025-11-17 |
| Nunchuk | yes | · | 2026-02-20 |
| BlueWallet | yes | · | bundles the SilentPayments library |
| Electrum | third party plugin, unaudited | · | 2026-07-26 |
| Bitcoin Core | no | no | three wallet pull requests still open |
| BTCPay Server | no | no | one unanswered request since 2025-12-21 |
The cryptography reached a tagged release of libsecp256k1 on 3 August 2026, in version 0.8.0, four years and four months after the idea was posted to a mailing list. Bitcoin Core still ships nothing to users. That gap is the honest state of play, and it is why a name that a human can type matters: the protocol does not fail on mathematics, it fails on a 116 character string nobody will ever read out.
One number you will not find here, because it cannot exist: how many silent payments have been made. The outputs are ordinary taproot outputs with no marker, so nobody, including chain analysis firms, can count them. If somebody quotes you a figure, it was invented.
Sources
- BIP 352, the specification
- reference.py, the reference implementation
- The 28 test vectors
- BIP 374, discrete log equality proofs
- BIP 375, sending with PSBTs
- Frigate, and its benchmark numbers
- libsecp256k1 0.8.0 changelog
- Bitcoin Core tracking issue 28536
- Light client protocol thread
- Bitcoin Optech topic page
- Ruben Somsen’s original gist
- Wallet support tracker
Keep reading