Last active
December 4, 2025 18:43
-
-
Save theStack/25c77747838610931e8bbeb9d76faf78 to your computer and use it in GitHub Desktop.
Different approaches for scanning in Silent Payments (BIP-352)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Different approaches for scanning in Silent Payments (BIP-352) | |
| ============================================================================================= | |
| ===== "calculate label candidates for each tx output, look them up in the labels cache" ===== | |
| ============================================================================================= | |
| The current approach for scanning in secp #1765 is as follows, following BIP-352: | |
| For each k value, derive the unlabeled output candidate P_k, then loop over the | |
| transaction x-only outputs linearly to check whether there is either a unlabeled or a | |
| labeled match. For the latter, two label candidates are calculated for each transaction | |
| output (necessary as we don't know the correct Y coordinate for x-only outputs), that are | |
| checked against the labels cache each to see if there is a match: | |
| ------------------------------------------------------------ | |
| for k = 0..: | |
| derive P_k | |
| for tx_output in tx_outputs: | |
| if pk_xonly(P_k) == tx_output: | |
| UNLABELED_MATCH | |
| label_candidate1 = pk_even(tx_output) - P_k | |
| label_candidate2 = pk_odd(tx_output) - P_k | |
| if any of label_candidate{1,2} in labels_cache: | |
| LABELED_MATCH | |
| abort scanning if there was no match | |
| ------------------------------------------------------------ | |
| For scanning a transaction that doesn't have any matches (the common case), there are | |
| 2 * N_OUTPUTS point additions needed for labels scanning, independent on the number of | |
| different labels to scan for. | |
| ======================================================================================== | |
| ===== "calculate output candidates for each label, look them up in the tx outputs" ===== | |
| ======================================================================================== | |
| Alternative approach (as proposed by w0xlt): | |
| For each k value, derive the unlabeled output candidate P_k (that part is still the same). | |
| Instead of looping linearly over all the transaction outputs and calculating label | |
| candidates for each of them, let the user supply all the label public keys to scan for, | |
| and calculate the output candidate from that, then look for each label if there is a match: | |
| ------------------------------------------------------------ | |
| for k = 0..: | |
| derive P_k | |
| if pk_xonly(P_k) in tx_outputs: | |
| UNLABELED_MATCH | |
| for label_pubkey in user_supplied_labels: | |
| output_candidate = P_k + label_pubkey | |
| if pk_xonly(output_candidate) in tx_outputs: | |
| LABELED_MATCH | |
| abort scanning if there was no match | |
| ------------------------------------------------------------ | |
| The lookup in tx_outputs can be sped up by using binary search (relevant for worst-case | |
| scanning attack). For scanning a transaction that doesn't have any matches (the common | |
| case), there are N_LABELS point additions needed for labels scanning. | |
| Especially for users with a small number of used labels (i.e. only the label for change | |
| outputs is used, i.e. N_LABELS = 1), the second approach seems significanly faster than | |
| the original one, considering that most Silent Payments eligible transactions will likely have | |
| at least two outputs to scan for. As the number of labels to scan for increases, the first | |
| approach is likely faster. | |
| Any thoughts on this? Did I miss anything? |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Benchmark results from the WIP "silentpayments" secp256k1 module (theStack/secp256k1@8eced64) over a L/N matrix (note that L=1 should represent any higher L values as well for the BIP approach, assuming that the label cache lookup cost can be neglected):