Subject: [bitcoin-dev] BIP for Silent Payments

From: josibake <[email-masked]>
Date: Mon, 05 Jun 2023 16:21:06 +0000

Hi all,
After what has seemed like an eternity of refining and tweaking (pun intended) the original write-up proposed by Ruben Somsen back in March 2022, we'd like to submit a Silent Payments BIP for your consideration and review.

For convenience, the overview from the BIP is in plain text below. Please note: the overview is not the full specification; the full specification can be found at https://github.com/bitcoin/bips/pull/1458/files , along with a WIP implementation for Bitcoin Core at https://github.com/bitcoin/bitcoin/pull/27827 .

Looking forward to hearing your thoughts!

-- Ruben, Josie

== Silent Payments ==

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. 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

== Goals ==

We aim to present a protocol which satisfies the following properties:

-   No increase in the size or cost of transactions

-   Resulting transactions blend in with other bitcoin transactions and can’t be distinguished
-   Transactions can’t be linked to a silent payment address by an outside observer
-   No sender-receiver interaction required
-   No linking of multiple payments to the same sender
-   Each silent payment goes to a unique address, avoiding accidental address reuse
-   Supports payment labeling
-   Uses existing seed phrase or descriptor methods for backup and recovery
-   Separates scanning and spending responsibilities
-   Compatible with other spending protocols, such as CoinJoin
-   Light client/SPV wallet support
-   Protocol is upgrade-able

== Overview ==

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, and G represents the generator point 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.

=== 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:

-   Let P = hash(a·B)·G + B
-   Encode P as a BIP341 taproot output

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 (e.g. calculating P = hash(b·A)·G + B 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, we included an integer in the following manner:

-   Let n = 0
-   Let P0 = hash(a·B || n)·G + B
-   For additional outputs:
    -   Increment n by one (n++)
    -   Let Pi = hash(a·B || n)·G + B

Bob detects this output the same as before by searching for P0 = hash(b·A || 0)·G + B. Once he detects the first output, he must:

-   Check for P1 = hash(b·A || 1)·G + B
-   If P1 is not found, stop
-   If P1 is found, continue to check for P2 and so on until an additional output is not found

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.

=== 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 destination P. To prevent this, Alice should include a hash of the outpoint in the following manner:

-   Let outpoint_hash = hash(txid || vout)
-   Let P0 = hash(outpoint_hash·a·B || 0)·G + B

Bob must include the same outpoint_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[3]. This significantly reduces Bob's scanning requirement, makes light client support more feasible[4], and protects Alice's privacy in collaborative transaction protocols such as CoinJoin[5].

Alice performs the tweak with the sum of her input private keys in the following manner:

-   Let outpoints_hash = hash(txid0 || vout0 || … txidn || voutn)
-   Let a = a0 + a1 … + an
-   Let P0 = hash(outpoints_hash·a·B || 0)·G + B

=== 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 (Bscan, Bspend). This allows Bob to keep bspend in offline cold storage and perform the scanning with the public key Bspend and private key bscan. Alice performs the tweak using both of Bob’s public keys in the following manner:

-   Let P0 = hash(outpoints_hash·a·Bscan || 0)·G + Bspend

Bob detects this payment by calculating P0 = hash(outpoints_hash·bscan·A)·G + Bspend with his online device and can spend from his cold storage signing device using (hash(outpoints_hash·bscan·A) + bspend) mod p as the private key.

=== Labels ===

For a single silent payment address of the form (Bscan, Bspend), 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 Bspend with an integer m in the following way:

-   Let Bm = Bspend + m·G
-   Publish (Bscan, B0), (Bscan, B1) …

Alice performs the tweak as before using one of the published (Bscan, Bm) pairs. Bob detects the labeled payment in the following manner:

-   Let P0 = hash(outpoints_hash·bscan·A || 0)·G + Bspend
-   Subtract P0 from each of the transaction outputs and check if the remainder matches any of the labels (1·G, 2·G ..) that the wallet has previously used

It is important to note that an outside observer can easily deduce that each published (Bscan, Bm) pair is owned by the same entity as each published address will have Bscan 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. To do so, he can reserve a secret change label in the following manner:

-   Let Bchange = Bspend + hash(bscan)·G

Now, whenever Bob is spending (to a silent payment address or otherwise), he can create a change output for himself using the silent payments protocol and his change label in the following manner:

-   Let a = a0 + a1 … + an represent the private keys of the inputs Bob is using to fund the transaction
-   Let Pchange = hash(outpoints_hash·a·Bchange || 0)·G + Bspend

This gives Bob an alternative to using BIP32 for managing change, while still allowing him to know which of his unspent outputs were change when recovering his wallet from the master key. The change label needs to remain a secret in order to ensure nobody else can label payments as change.

Sent with Proton Mail secure email.