0352 - Proposed - Standards - March 9, 2023 (3 years, 4 months ago)
9
2023
BIP: 352
Layer: Applications
Title: Silent Payments
Author: josibake <[email protected]>
Ruben Somsen <[email protected]>
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP 352
Status: Proposed
Type: Standards Track
Created: 2023-03-09
License: BSD-2-Clause
Post-History: 2022-03-13: https://gist.github.com/RubenSomsen/c43b79517e7cb701ebf77eec6dbb46b8 [gist] Original proposal
2022-03-28: https://gnusha.org/pi/bitcoindev/CAPv7TjbX[email protected]/ [bitcoin-dev] Silent Payments – Non-interactive private payments with no on-chain overhead
2022-10-11: https://gnusha.org/pi/bitcoindev/P_21MLHGJicZ-hkbC4DGu86c5BtNKiH8spY4TOw5FJsfimdi_6VyHzU_y-s1mZsOcC2FA3EW_6w6W5qfV9dRK_7AvTAxDlwVfU-yhWZPEuo=@protonmail.com/ [bitcoin-dev] Silent Payment v4 (coinjoin support added)
2023-08-04: https://gnusha.org/pi/bitcoindev/[email protected]/ [bitcoin-dev] BIP 352 Silent Payments addresses should have an expiration time
This document specifies a protocol for static payment addresses in Bitcoin without on-chain linkability of payments or a need for on-chain notifications.
This BIP is licensed under the BSD 2-clause license.
Using a new address for each Bitcoin transaction is a crucial aspect of maintaining privacy. This often requires a secure interaction between sender and receiver, so that the receiver can hand out a fresh address, a batch of fresh addresses, or a method for the sender to generate addresses on-demand, such as an xpub.
However, interaction is often infeasible and in many cases undesirable. To solve for this, various protocols have been proposed which use a static payment address and notifications sent via the blockchain 1 . These protocols eliminate the need for interaction, but at the expense of increased costs for one-time payments and a noticeable footprint in the blockchain, potentially revealing metadata about the sender and receiver. Notification schemes also allow the receiver to link all payments from the same sender, compromising sender privacy.
This proposal aims to address the limitations of these current approaches by presenting a solution that eliminates the need for interaction, eliminates the need for notifications, and protects both sender and receiver privacy. These benefits come at the cost of requiring wallets to scan the blockchain in order to detect payments. This added requirement is generally feasible for full nodes but poses a challenge for light clients. While it is possible today to implement a privacy-preserving light client at the cost of increased bandwidth, light client support is considered an area of open research (see Appendix A: Light Client Support ).
The design keeps collaborative transactions such as CoinJoins and inputs with MuSig and FROST keys in mind, but 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.
We aim to present a protocol which satisfies the following properties:
We first present an informal overview of the protocol. In what follows, uppercase letters represent public keys, lowercase letters represent private keys, || refers to byte concatenation, · refers to elliptic curve scalar multiplication, G represents the generator point for secp256k1, and n represents the curve order for secp256k1. Each section of the overview is incomplete on its own and is meant to build on the previous section in order to introduce and briefly explain each aspect of the protocol. For the full protocol specification, see Specification .
Simple case
Bob publishes a public key B as a silent payment address. Alice discovers Bob's silent payment address, selects a UTXO with private key a , public key A and creates a destination output P for Bob in the following manner:
Since a·B == b·A ( Elliptic-curve Diffie–Hellman ), Bob scans with his private key b by collecting the input public keys for each transaction with at least one unspent taproot output and performing the ECDH calculation until P is found (i.e. calculating P = B + hash(b·A)·G and seeing that P is present in the transaction outputs).
Creating more than one output
In order to allow Alice to create more than one output for Bob 2 , we include an integer in the following manner:
Bob detects this output the same as before by searching for P 0 = B + hash(b·A || 0)·G . Once he detects the first output, he must:
Since Bob will only perform these subsequent checks after a transaction with at least one output paying him is found, the increase to his overall scanning requirement is negligible. It should also be noted that the order in which these outputs appear in the transaction does not affect the outcome.
Preventing address reuse
If Alice were to use a different UTXO from the same public key A for a subsequent payment to Bob, she would end up deriving the same destinations P i . To prevent this, Alice should include an input hash in the following manner:
Bob must calculate the same input_hash when scanning.
Using all inputs
In our simplified example we have been referring to Alice's transactions as having only one input A , but in reality a Bitcoin transaction can have many inputs. Instead of requiring Alice to pick a particular input and requiring Bob to check each input separately, we can instead require Alice to perform the tweak with the sum of the input public keys 4 . This significantly reduces Bob's scanning requirement, makes light client support more feasible 5 , and protects Alice's privacy in collaborative transaction protocols such as CoinJoin<ref name=""all_inputs_and_coinjoin"> Why does using all inputs matter for CoinJoin? If Alice uses a random input to create the output for Bob, this necessarily reveals to Bob which input Alice has control of. If Alice is paying Bob as part of a CoinJoin, this would reveal which input belongs to her, degrading the anonymity set of the CoinJoin and giving Bob more information about Alice. If instead all inputs are used, Bob has no way of knowing which input(s) belong to Alice. This comes at the cost of increased complexity as the CoinJoin participants now need to coordinate to create the silent payment output and would need to use Blind Diffie–Hellman to prevent the other participants from learning who Alice is paying. Note it is currently not recommended to use this protocol for CoinJoins due to a lack of a formal security proof.
.
Alice performs the tweak with the sum of her input private keys in the following manner:
Spend and Scan Key
Since Bob needs his private key b to check for incoming payments, this requires b to be exposed to an online device. To minimize the risks involved, Bob can instead publish an address of the form (B scan , B spend ) . This allows Bob to keep b spend in offline cold storage and perform the scanning with the public key B spend and private key b scan . Alice performs the tweak using both of Bob's public keys in the following manner:
Bob detects this payment by calculating P 0 = B spend + hash(input_hash·b scan ·A || 0)·G with his online device and can spend from his cold storage signing device using (b spend + hash(input_hash·b scan ·A || 0)) mod n as the private key.
Labels
For a single silent payment address of the form (B scan , B spend ) , Bob may wish to differentiate incoming payments. Naively, Bob could publish multiple silent payment addresses, but this would require him to scan for each one, which becomes prohibitively expensive. Instead, Bob can label his spend public key B spend with an integer m in the following way:
Alice performs the tweak as before using one of the published (B scan , B m ) pairs. Bob detects the labeled payment in the following manner:
It is important to note that an outside observer can easily deduce that each published (B scan , B m ) pair is owned by the same entity as each published address will have B scan in common. As such, labels are not meant as a way for Bob to manage separate identities, but rather a way for Bob to determine the source of an incoming payment.
Labels for change
Bob can also use labels for managing his own change outputs. We reserve m = 0 for this use case. This gives Bob an alternative to using BIP 32 for managing change, while still allowing him to know which of his unspent outputs were change when recovering his wallet from the master key. It is important that the wallet never hands out the label with m = 0 in order to ensure nobody else can create payments that are wrongly labeled as change.
While the use of labels is optional, every receiving silent payments wallet should at least scan for the change label when recovering from backup in order to ensure maximum cross-compatibility.
We use the following functions and conventions:
COutPoint
of an input
(32-byte txid, least significant byte first || 4-byte vout, least
significant byte first)
7
For everything not defined above, we use the notation from BIP 340 . This includes the hash tag (x) notation to refer to SHA256(SHA256(tag) || SHA256(tag) || x) .
This document defines version 0 ( sp1q ). Version is communicated through the address in the same way as bech32 addresses (see BIP 173 . Future upgrades to silent payments will require a new version. As much as possible, future upgrades should support receiving from older wallets (e.g. a silent payments v0 wallet can send to both v0 and v1 addresses). Any changes that break compatibility with older silent payment versions should be a new BIP.
Future silent payments versions will use the following scheme:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | Compatibility | |
|---|---|---|---|---|---|---|---|---|---|
|
+0 |
q |
p |
z |
r |
y |
9 |
x |
8 |
backwards compatible |
|
+8 |
g |
f |
2 |
t |
v |
d |
w |
0 |
|
|
+16 |
s |
3 |
j |
n |
5 |
4 |
k |
h |
|
|
+24 |
c |
e |
6 |
m |
u |
a |
7 |
- |
v31 (l) is reserved for a backwards incompatible change, if needed. For silent payments v0:
For silent payments v0 a transaction MUST be scanned if and only if all of the following are true:
A silent payment address is constructed in the following manner:
Note: BIP 173 imposes a 90 character limit for Bech32 segwit addresses and limits versions to 0 through 16, whereas a silent payment address requires at least 117 characters 11 and allows versions up to 31. Additionally, since higher versions may add to the data field, it is recommended implementations use a limit of 1023 characters (see BIP 173: Checksum design for more details).
While any UTXO with known output scripts can be used to fund the transaction, the sender and receiver MUST use inputs from the following list when deriving the shared secret:
Inputs with conditional branches or multiple public keys (e.g. CHECKMULTISIG ) are excluded from shared secret derivation as this introduces malleability and would allow a sender to re-sign with a different set of public keys after the silent payment output has been derived. This is not a concern when the sender controls all of the inputs, but is an issue for CoinJoins and other collaborative protocols, where a malicious participant can participate in deriving the silent payment address with one set of keys and then re-broadcast the transaction with signatures for a different set of public keys. P2TR can have hidden conditional branches (script path), but we work around this by using only the output public key.
For all of the output types listed, only X-only and compressed public keys are permitted 12 .
P2TR
Keypath spend
witness:
scriptSig: (empty)
scriptPubKey: 1 <32-byte-x-only-key>
(0x5120{32-byte-x-only-key})
The sender uses the private key corresponding to the taproot output key (i.e. the tweaked private key). This can be a single private key or an aggregate key (e.g. taproot outputs using MuSig or FROST) 13 . The receiver obtains the public key from the scriptPubKey (i.e. the taproot output key).
Script path spend
witness:
scriptSig: (empty)
scriptPubKey: 1 <32-byte-x-only-key>
(0x5120{32-byte-x-only-key})
Same as a keypath spend, the sender MUST use the private key corresponding to the taproot output key. If this key is not available, the output cannot be included as an input to the transaction. Same as a keypath spend, the receiver obtains the public key from the scriptPubKey (i.e. the taproot output key) 14 .
The one exception is script path spends that use NUMS point H as their internal key (where H is constructed by taking the hash of the standard uncompressed encoding of the secp256k1 base point G as X coordinate, see BIP 341: Constructing and spending Taproot outputs for more details), in which case the input will be skipped for the purposes of shared secret derivation 15 . The receiver determines whether or not to skip the input by checking in the control block if the taproot internal key is equal to H .
P2WPKH
witness:
<33-byte-compressed-key>
scriptSig: (empty)
scriptPubKey: 0 <20-byte-key-hash>
(0x0014{20-byte-key-hash})
The sender performs the tweak using the private key for the output and the receiver obtains the public key as the last witness item.
P2SH-P2WPKH
witness:
<33-byte-compressed-key>
scriptSig: <0 <20-byte-key-hash>>
(0x160014{20-byte-key-hash})
scriptPubKey: HASH160 <20-byte-script-hash> EQUAL
(0xA914{20-byte-script-hash}87)
The sender performs the tweak using the private key for the nested P2WPKH output and the receiver obtains the public key as the last witness item.
P2PKH
scriptSig:
<33-byte-compressed-key>
scriptPubKey: OP_DUP HASH160 <20-byte-key-hash> OP_EQUALVERIFY OP_CHECKSIG
(0x76A914{20-byte-key-hash}88AC)
The receiver obtains the public key from the
scriptSig
. The
receiver MUST parse the
scriptSig
for the public key, even if
the
scriptSig
does not match the template specified (e.g.
<dummy> OP_DROP <Signature> <Public Key>
).
This is to address the
third-party
malleability of
P2PKH
scriptSigs
.
The sending wallet performs coin selection as usual with the following restrictions:
After the inputs have been selected, the sender can create one or more outputs for one or more silent payment addresses in the following manner:
All generated outputs MUST be present in the final transaction. If an output P i with i < k is omitted, the receiver will not be able to find outputs P j where i < j <= k .
Two keys are needed to create a silent payments address: the spend key and the scan key. To ensure compatibility, wallets MAY use BIP 32 derivation with the following derivation paths for the spend and scan key. When using BIP 32 derivation, wallet software MUST use hardened derivation 19 for both the spend and scan key.
A scan and spend key pair using BIP 32 derivation are defined (taking inspiration from BIP 44 ) in the following manner:
scan_private_key: m / purpose' / coin_type' / account' / 1' / 0
spend_private_key: m / purpose' / coin_type' / account' / 0' / 0
purpose
is a constant set to
352
following the
BIP 43 recommendation. Refer to
BIP
43
and
BIP
44
for more details.
If each of the checks in Scanning silent payment eligible transactions passes, the receiving wallet must:
Recall that a silent payment output is of the form B spend + t k ·G + hash BIP 352/Label (ser 256 (b scan ) || ser 32 (m))·G , where hash BIP 352/Label (ser 256 (b scan ) || ser 32 (m))·G is an optional label. To spend a silent payment output:
Since each silent payment output address is derived independently, regular backups are recommended. When recovering from a backup, the wallet will need to scan since the last backup to detect new payments.
If using a seed/seed phrase only style backup, the user can recover the wallet's unspent outputs from the UTXO set (i.e. only scanning transactions with at least one unspent taproot output) and can recover the full wallet history by scanning the blockchain starting from the wallet birthday. If a wallet uses labels, this information SHOULD be included in the backup. If the user does not know whether labels were used, it is strongly recommended they always precompute and check a large number of labels (e.g. 100k labels) to use when re-scanning. This ensures that the wallet can recover all funds from only a seed/seed phrase backup. The change label should simply always be scanned for, even when no other labels were used. This ensures the use of a change label is not critical for backups and maximizes cross-compatibility.
Silent payments introduces a new address format and protocol for sending and as such is not compatible with older wallet software or wallets which have not implemented the silent payments protocol.
A collection of test vectors in JSON format are provided, along with a python reference implementation . Each test vector consists of a sending test case and corresponding receiving test case. This is to allow sending and receiving to be implemented separately. To ensure determinism while testing, sort the array of B m by amount (see the reference implementation ). Test cases use the following schema:
test_case
{
"comment": "Comment describing the behavior being tested",
"sending": [
],
"receiving": [
],
}
sender
{
"given": {
"vin": [<array of vin objects with an added field for the private key. These objects are structured to match the `vin` output field from `getrawtransaction verbosity=2`>],
"recipients": [<array of strings, where each string is a bech32m encoding representing a silent payment address>]
},
"expected": {
"outputs": [<array of strings, where each string is a hex encoding of 32-byte X-only public key; contains all possible output sets, test must match a subset of size `n_outputs`>],
"n_outputs":
,
},
}
recipient
{
"given": {
"vin": [<array of vin objects. These objects are structured to match the `vin` output field from `getrawtransaction verbosity=2`>],
"key_material": {
"scan_priv_key":
,
"spend_priv_key":
,
}
"labels": [<array of ints, representing labels the receiver has used>],
},
"expected": {
"addresses": [<array of bech32m strings, one for the silent payment address and each labeled address (if used)>],
"outputs": [<array of outputs with tweak and signature; contains all possible output sets, tester must match a subset of size `n_outputs`>
{
"priv_key_tweak":
,
"pub_key":
,
"signature": <hex encoded signature for the output (produced with spend_priv_key + priv_key_tweak)>
},
...
],
"n_outputs":
}
}
Wallets should include inputs not in the Inputs For Shared Secret Derivation list when testing to ensure that only inputs from the list are being used for shared secret derivation. Additionally, receiving wallets should include non-silent payment outputs for themselves in testing to ensure silent payments scanning does not interfere with regular outputs detection.
Below is a list of functional tests which should be included in sending and receiving implementations.
This section proposes a few ideas for how light clients could support scanning for incoming silent payments (sending is fairly straightforward) in ways that preserve bandwidth and privacy. While this is out of scope for the current BIP, it is included to motivate further research into this topic. In this context, a light client refers to any bitcoin wallet client which does not process blocks and does not have a direct connection to a node which does process blocks (e.g. a full node). Based on this definition, clients that directly connect to a personal electrum server or a bitcoin node are not light clients.
This distinction makes the problem for light clients more clear: light clients need a way to source the necessary data for performing the tweaks and a way of determining if any of the generated outputs exist in a block.
Recall that a silent payment eligible transaction follows certain conditions and should have at least one unspent taproot output. Full nodes (or any index server backed by a full node, such as electrum server) can build an index which collects all of the eligible public keys for a silent payments eligible transaction, sums them up, multiplies the sum by the input_hash , and serves them to clients. This would be 33 bytes per silent payment eligible transaction.
For a typical bitcoin block of ~3500 txs, lets assume every transaction is a silent payments eligible transaction. This means a client would need to request 33 bytes * 3500 of data per block (roughly 100 kB per block). If a client were to request data for every block, this would amount to ~450 MB per month, assuming 100% taproot usage and all non-dust outputs remain unspent for > 1 month. As of today, these numbers are closer to 7–12 kB per block (30–50 MB per month) 23 .
It is unlikely a light client would need to scan every block and as such can take advantage of transaction cut-through, depending on how often they choose to scan for new blocks. Empirically, ~75% of transactions with at least one non-dust unspent taproot output will have spent all non-dust taproot UTXOs in 150 blocks or less 24 . This means a client that only scans once per day could significantly cut down on the number of blocks and the number of transactions per block that they need to request by only asking for data on transactions that were created since their last scan and that still have at least one non-dust unspent taproot output as of the current block height. Based on taproot adoption as of July 2024, a light client scanning once every 3 days would use roughly 30 MB per month .
Once a light client has the tweak data for a block, they can determine whether or not an output to them exists in the block using BIP 158 block filters. Per BIP 158, they would then request the entire block and add the transaction to their wallet, though it maybe be possible to only request the prevout txids and vouts for all transactions with at least one taproot output, along with the scriptPubKeys and amounts. This would allow the client to download the necessary data for constructing a spending transaction, without downloading the entire block. How this affects the security assumptions of BIP 158 is an open question.
Assuming a secure messaging protocol exists, the sender can send an encrypted (using the scan public key of the silent payment address) notification to the receiver with the following information:
It is important to note that these notifications are not required. At any point, the receiver can fall back to scanning for silent payment transactions if they don't trust the notifications they are receiving, are being spammed with fake notifications, or if they are concerned that they are not receiving notifications.
A malicious notification could potentially cause the following issues:
Wallet designers can choose which tradeoffs they find appropriate. For example, a wallet could check the block filter to at least probabilistically confirm the likely existence of the UTXO, thus efficiently cutting down on spam. The payment could then be marked as unconfirmed until a scan is performed and the existence of the UTXO in accordance to the silent payment specification is verified.
To help implementers understand updates to this document, we attach a
version number that resembles
semantic versioning
(
MAJOR.MINOR.PATCH
). The
MAJOR
version is
incremented if changes to the BIP are introduced that are incompatible
with prior versions. The
MINOR
version is incremented
whenever the inputs or the output of an algorithm changes in a
backward-compatible way or new backward-compatible functionality is
added. The
PATCH
version is incremented for other changes
that are noteworthy (bug fixes, test vectors, important clarifications,
etc.).
This document is the result of many discussions and contains contributions by a number of people. The authors wish to thank all those who provided valuable feedback and reviews, including the participants of the BIP 47 Prague discussion , the Advancing Bitcoin silent payments Workshop , and coredev . The authors would like to also thank w0xlt for writing the initial implementation of silent payments.
Why do silent payment addresses need at least 117 characters? A silent payment address is a bech32m encoding comprised of the following parts:
For a silent payments v0 address, this results in a 117-character address when using a 3-character HRP. Future versions of silent payment addresses may add to the payload, which is why a 1023-character limit is suggested. ↩︎