Bitcoin is in constant evolution and one of its recent attraction is around silent payments.š¤«.
In this post we will serve a simplified, high level description of how silent payments work , their advantages over existing methods, how you can use them and finally do a practical case on receiving and scanning silent payment outputs using Frigate and BDK
(Make sure there's no noise around you before we start...)
Silent payment is a protocol defined by BIP 352 which introduces static payment addresses in Bitcoin. Compared to previous approaches this comes with the following advantages:
- no onchain linkability
- no need of onchain notifications
- preserve the privacy of the sender and receiver which is a fundamental aspect of Bitcoin.
However the afored mentioned benefits come with a cost we will discuss later.
Before SP, in order to receive a payment, there has to be interaction between the sender and receiver every single time, since to preserve his privacy the receiver would have to generate a new fresh address and hand it over the sender. However, this could lead to bad user experience and operational difficulties. For example assume you are collecting donations for your Charity, you'll have to operate a server like BTCPay for generating new addresses for every donors for preserving privacy.
SP come handy because it uses a static address from which a fresh unused public key is generated for each transaction that sends to it. Even though the address is reused, only the sender and recipient can identify the transactions made to it. For achieving that the protocol uses some interesting cryptography techniques among them ECDH.
this section goes a little down explaining the ECDH cryptography function and assumes some prior knowledge to ECDSA notation and functionnig
One of the magic behind the inner working of SP resides on the powerful ECDH key agreement protcol, which helps in generating shared secret between two parties.
Let's setup some context to better understand how the shared secret generation works:
- Alice (the sender) has a private key/public key pair:
a * G = A, whereais private key,Gthe generator point - Bob (the recipient) has a private key/public key pair:
b * G = B, wherebis private key,Gthe generator point
Now in order to generate the shared secret, Alice and Bob need to exchange their public keys: A and B.
Once Alice receives Bob public key she then computes a secret S = a * B likewise Bob computes S = b * A.
Since the shared secret computed should be same, it meansa * B = b * A. Substituing B by b * G we get: a * (b * G) = b * A.
Given the associativity of the elliptic curve scalar multiplication, we can write: b * (a * G) = b * A which finally prooves the equation b * A = b * A!
[Diagram summarizing the ECDH process]
Now you may be wondering how does this relates to SP.
In fact the shared secret will be used inside the equation locking the output to the receiver public key.
Let's call the locking public key P. As defined in BIP 352 specification, P = B + hash(a * B) * G, spotted the shared secret ?
The resulting value of P is what will be put in the Taproot output of the transaction.
Notice that the locking equation has two B. Without the additional added B (means writing P = hash(a * B) * G) , coins locked by the public key (P) would still be spendable by either sender Alice (using her private key a) or recipient Bob (using his private key b).
For improved security, the protocol requires the first and the second B to be completly different value. The first one called spend key,B_spend with its associated private key b_spend, and the second is now called scan key B_scan with it's associated private key b_scan. So the equation becomes P = B_spend + hash(a * B_scan) * G.
The reason to this is, the private key b of Bob will need to exposed to an online device to check for incoming payments.
[In order to avoid any potential private key leak, the responsibilities are separated into two keys, one that's meant only for scanning and another for spending.] We will see how that separation of concerns helps in the scanning section.
The combination of the spend and scan key will now form what we call a silent payment address.
An example of silent payment address: sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv. The address is longer than traditional Bitcoin addresses, this is because it encodes:
- A human readable part and sepator
sp1 - The version
0encoded asq - Scanning and Spending public keys
- And finally a checksum
There's a little bit more to know and understand about how SP work we just skimmed through the surface here, for more in depth details I'd recommend checking the official BIP 352 specification
- Using BIP 32 Extended public keys: By sharing the extended public key (xpub) of an entire bitcoin account, the sender can create new addresses on their own. This only solves the problem for single transaction partners at a time, though, because sharing a single extended public key with multiple entities would, again, be a major privacy compromise.
- BIP 47 defines reusable payment codes. While they preserve privacy and remove the burden on requiring interaction for every new transaction, their main downside is that for sending a transaction the sender needs to send a notification transaction to the notification address, containing some information in an OP_RETURN output.
As stated at the beginning of the post the benefits of SP come with a cost: The receiver needs to scan the blockchain everytime for detecting payments made to his address. As such this will be a bottleneck for light clients because they don't have the required power nor resources for bearing the cost of constantly querying the blockchain.
In order to mitigate that several solutions defined by the Work In Progress index server specification for BIP 352 have emerged namely: remote scanner, Tweak server, My Scanner a perfonalized scanner. A comparison between the different method from privacy to operational cost can be found here.
Frigate is an experimental Electrum Server testing Silent Payments scanning with ephemeral client keys implementing the remote scanner strategy. It builds on top of the traditional electrum rpc protocol and adds additional RPC methods for requesting SP information from a server. It uses an efficient in database technique for fast query and processing, and can also if requested takes advantage of the GPU for reducing scanning time.
For this to work, the client has to provide both the scan private key (b_scan) and the spend public key (B_spend). The private key are never stored, only held in memory thus ephemeral.
For this practical we will use the bdk-sp experimental repository. It provides a wallet with support of sending and receiving SP transactions.
We first need to get every thing up for running: Follow these links to install podman and just, which are required depencencies to make things work.
Afterwards clone the repository:
git clone --depth 1 https://github.com/sdmg15/bdk-sp --branch feat/frigateOnce cloned head over the file frigate_playbook.sh. This file contains the necessary commands that we will need to executed in order to create and scan for outputs.
The playbook is organized into stages each stages with steps that needs to be executed. The playbook is self explanatory of the different process.
Let's follow the playbook and start by creating a silent payment output.
The just non_nix_init command could take several minutes to complete depending, so take a cup of coffee in the meantimeā¦
# 1. Install dependencies locally and setup regtest environment
just non_nix_init
# 2. Check bitcoind is running on regtest
just cli getblockchaininfo
# 3. Check bdk-cli wallet was created correctly
just regtest-bdk balance
# 4. Check sp-cli wallet was created correctly
just regtest-sp balance
# 5. Synchronize bdk-cli wallet
just regtest-bdk sync# 6. Get a new address from bdk-cli wallet
REGTEST_ADDRESS=$(just regtest-bdk unused_address | jq -r '.address' | tr -d '\n')
# 7. Mine a few more blocks to fund the wallet
just mine 1 $REGTEST_ADDRESS
# 8. Mine some of them to the internal wallet to confirm the bdk-cli balance
just mine 101
# 9. Synchronize bdk-cli wallet
just regtest-bdk sync
# 10. Check balance
just regtest-bdk balance# 11. Get a silent payment code from sp-cli2 wallet
SP_CODE=$(just regtest-sp code | jq -r '.silent_payment_code' | tr -d '\n')
# 12. Create a transaction spending bdk-cli wallet UTXOs to a the previous silent payment code
RAW_TX=$(just regtest-bdk create_sp_tx --to-sp $SP_CODE:10000 --fee_rate 5 | jq -r '.raw_tx' | tr -d '\n')
TXID=$(just regtest-bdk broadcast --tx $RAW_TX | jq -r '.txid' | tr -d '\n')
# 14. Mine a new block
just mine 1
# 15. Once the new transaction has been mined, synchronize bdk-cli wallet again
just regtest-bdk sync# 16. Now synchronize sp-cli2 wallet using frigate ephemeral scanning
FRIGATE_HOST="127.0.0.1:57001"
START_HEIGHT=1
just regtest-sp scan-frigate --url $FRIGATE_HOST --start $START_HEIGHT
# 17. Check balance on sp-cli2 wallet
just regtest-sp balance
# 18. Check balance on bdk-cli wallet
just regtest-bdk balanceIf you reached here congrats ! You succeeded in sending and scanning a silent payment output !
The discussions around SP are really interesting and ongoing. Developers working on it are constantly finding ways of keeping the balance between privacy and user experience. There is still some good area of research, if interested I'd invite you to join the different conversations, reading the BIPs and helping making this feature even better !
1- https://github.com/bitcoin/bips/blob/master/bip-0352.mediawik
2- https://github.com/silent-payments/BIP0352-index-server-specification
3- https://blog.bitbox.swiss/en/understanding-silent-payments-part-one/