This PR adds a new Silent Payments (BIP352) module to secp256k1. The following routines are provided ($a_i$ are input private keys, $A_i$ are input public keys, $b$ and $B$ denote recipient privkeys/pubkeys that would be encoded in silent payment addresses, $d$ and $P$ the keypair for the actual transaction taproot x-only output):
Side | Function | Inputs | Outputs |
---|---|---|---|
Sender | _create_private_tweak_data |
$a_1…a_n$, $outpoint_L$ | $a_{sum} = (a_1 + a_2 + … + a_n)$$inputhash = hash_I(outpoint_L || (a_{sum} * G))$ |
Receiver | _create_public_tweak_data |
$A_1…A_n$, $outpoint_L$ | $A_{sum} = (A_1 + A_2 + … + A_n)$$inputhash = hash_I(outpoint_L || A_{sum})$ |
Receiver | _create_tweaked_pubkey |
$A_{sum}, inputhash$ | $A_{tweaked} = inputhash * A_{sum}$ |
Both | _create_shared_secret |
$Pub$, $sec$(Sender: $B_{scan}, a_{sum}$Receiver: $A_{sum}, b_{scan}$Lightclient: $A_{tweaked}, b_{scan}$) | $SS = (inputhash * sec) * Pub$ (ECDH) |
Receiver | _create_label_tweak |
$b_{scan}, m$ | $labeltweak = hash_L(b_{scan} || m)$$label = labeltweak * G$ |
Receiver | _create_address_spend_pubkey |
$B_{spend}, label$ | $B_m = B_{spend} + label$ |
Sender | _sender_create_output_pubkey |
$SS, B_m, k$ | $P_{xonly} = B_m + hash_S(SS || k) * G$ |
Receiver | _receiver_scan_output |
$SS, B_m, k, tx_{output}$ | $t_k = hash_S(SS || k)$$P_{xonly} = B_m + t_k * G$ [not returned]$directmatch = P_{xonly} == tx_{output}$if $directmatch == 0$:$\quad label1 = tx_{output} - P$$\quad label2 = -tx_{output} - P$ |
Receiver | _create_output_seckey |
$b_{spend}, t_k, (labeltweak)$ | $d = (b_{spend} + labeltweak) + t_k$ |
where
- $hash_I$ denotes a SHA256 tagged hash with tag “BIP0352/Inputs”
- $hash_L$ denotes a SHA256 tagged hash with tag “BIP0352/Label”
- $hash_S$ denotes a SHA256 tagged hash with tag “BIP0352/SharedSecret”
For ending up with output key material used for sending to / scanning for / spending from, both sides would follow the chain of tweak_data -> shared_secret -> output key
. The public tweak data can be useful for faster scanning of output transactions by storing them in an index, see e.g. Bitcoin Core PR https://github.com/bitcoin/bitcoin/pull/28241. Private tweak data is arguably less useful, so in theory one could collapse the tweak data and shared secret creation functions into a single one, but IMHO it’s nicer if the API is symmetric.
As discussed in #1427 (comment), the approach of passing in two separate key pairs for taproot and non-taproot inputs is followed here. This may seem a bit confusing at first, but has the advantage that the caller doesn’t have to deal with enforcing even y-parity for key material manually (e.g. negating private keys of taproot outputs if they would end up in an odd point), which seems error-prone.
The last commit contains the BIP352 test vectors, converted to C code with a Python script. An earlier version of the tests, directly written in Python (by calling in to the secp256k1 shared library using ctypes) can still be found here: https://github.com/theStack/secp256k1/tree/silentpayments-module-demo_old5