BIP 352: Silent Payments #1458

pull josibake wants to merge 7 commits into bitcoin:master from josibake:silent-payments-bip changing 8 files +4497 −0
  1. josibake commented at 4:16 pm on June 5, 2023: member

    Silent payments is a static address protocol for Bitcoin, originally proposed on the mailing list here: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2022-March/020180.html

    Since then, the proposal has received several rounds of review and has a WIP implementation here: https://github.com/bitcoin/bitcoin/pull/27827 . The proposal has also been sent to the mailing list for review here: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-June/021750.html

    Proposing this as an informational BIP to ensure wallets across the ecosystem can standardize and correctly implement the protocol.

  2. josibake closed this on Jun 5, 2023

  3. josibake reopened this on Jun 5, 2023

  4. in bip-0000.mediawiki:91 in f0ccab189e outdated
    86+
    87+Bob must include the same ''outpoint_hash'' when scanning.
    88+
    89+''' Using all inputs '''
    90+
    91+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<ref name="other_inputs">'''What about inputs without public keys?''' Inputs without public keys can still be spent in the transaction but are simply ignored in the ''Silent Payments'' protocol.</ref>. This significantly reduces Bob's scanning requirement, makes light client support more feasible<ref name="using_all_inputs">'''How does using all inputs help light clients?''' If Alice uses a random input for the tweak, Bob necessarily has to have access to and check all transaction inputs, which requires performing an ECC multiplication per input. If instead Alice performs the tweak with the sum of the input public keys, Bob only needs the summed 32 byte public key per transaction and only does one ECC multiplication per transaction. Bob can then use BIP158 block filters to determine if any of the outputs exist in a block and thus avoids downloading transactions which don't belong to him.</ref>, 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 [https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406 Blind-Diffie-Hellman] to prevent the other participants from learning who Alice is paying</ref>.
    


    pinheadmz commented at 7:00 pm on June 5, 2023:
    I think you should either link to the light clients section below, or mention in this footnote that the 32-byte tweak for each TX in every block must be provided by a trusted source in addition to BIP158 data (If I understand correctly)

  5. josibake force-pushed on Jun 6, 2023
  6. in bip-0000.mediawiki:192 in f1c188faa5 outdated
    187+
    188+Inputs with conditional branches or multiple public keys (e.g ''CHECKMULTISIG'') are not included 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 that as described below.
    189+
    190+''' P2TR (key path spends)'''
    191+
    192+The sender MUST use the tweaked private key corresponding to the taproot output key (i.e. the tweaked private key for a key path spend). If the taproot output key is not available, the output cannot be used for sending a silent payment. The receiver always uses the taproot output key when scanning, regardless of whether the taproot output is being used in a key path spend or a script path spend<ref name="why_always_output_pubkey">''' Why not skip all taproot script path spends? ''' This causes malleability issues for CoinJoins. If the silent payments protocol skipped taproot script path spends, this would allow an attacker to join a CoinJoin round, participate in deriving the silent payment address using the tweaked private key for a key path spend, and then broadcast their own version of the transaction using the script path spend. If the receiver were to only consider key path spends, they would skip the attacker's script path spend input when deriving the shared secret and not be able to find the funds.</ref>.
    


    brandonblack commented at 9:40 pm on June 6, 2023:

    Could this be changed to “The sender MUST be able compute ax * Bscan on the tweaked private key (ax) corresponding to the taproot output key […]”. I believe that BIP327 (MuSig2) users can compute the correct value to be used with silent payments, but this phrase excludes them using it. This would also require changing the computation of P0 to something like:

    0let S = a0 * B + a1 * B + ... + an * B
    1let P0 = hash(outpoints_hash * S || 0) * G + B
    

    josibake commented at 2:32 pm on June 7, 2023:

    Glad to see this proposal advancing! I think with some minor changes it can be compatible with MuSig2 Taproot Keypaths (and potentially other off chain key aggregation schemes), which make it a powerful way of transacting privately even between users with advanced keys and scripts on Taproot.

    Thanks so much for the review! We definitely want to support key aggregation techniques (Musig2 / FROST / etc) on both the sender (inputs) and receiver (having the spend key be an aggregated key). I need to read up on BIP327 to make sure I fully understand your comments but will respond shortly. I’ve also responded in line to your other comments.


    RubenSomsen commented at 9:35 pm on June 7, 2023:

    Thanks for the comments. Is the concern that the way it is worded it sounds like the owner of an input is a single person while in reality it could be a group of owners using MuSig (or even FROST)? I do think it is worth specifically mentioning somewhere, but there shouldn’t be anything in the current specification that causes issues for such cases. Shared secrets are linear and so in e.g. a MuSig 2-of-2 the two people can both just calculate the shared secret (with their MuSig tweaked key) and add up the result.

    If these two people don’t trust each other then they also need to provide an discreet log equivalence proof (relatively simple) to prove they calculated the shared secret correctly. This detail probably is important enough to add to the BIP, now that I think about it.


    brandonblack commented at 9:59 pm on June 7, 2023:

    My concern was just the precise phrase MUST use the tweaked private key which does not exist in an aggregate key situation. So changing the phrasing be explicit about what computation is required ensures that users of complex keys are not incorrectly deterred. I think the approach for MuSig in particular would be something like…

    Participants compute and publish:

    0aB_px = d_px * B
    

    Coordinator computes:

    0untweaked_agg_pk = keyagg_coef_p1 * A_p1 + keyagg_coef_p2 * A_p2 + ... + keyagg_coef_pn * A_pn
    1aB_agg = keyagg_coef_p1 * aB_p1 + keyagg_coef_p2 * aB_p2 + ... + keyagg_coef_pn * aB_pn
    2aB_agg' = pointNegate(aB_agg) if !has_even_y(untweaked_agg_pk) else aB_agg
    3aB = aB_agg' + tapTweak * B
    

    (please don’t quote me on this computation, I think it’s close, but probably missed at least one detail)

    Of course the above is also complicated by x-only internal keys. yay.


    josibake commented at 12:42 pm on June 14, 2023:
    I’ve added a footnote and attempted to update the language to indicate key aggregation techniques are supported in https://github.com/bitcoin/bips/pull/1458/commits/d9e5a1b61dd464b808c8ebc45bbe0104ae8e5e14 @brandonblack let me know if you have any suggestions on how to improve it!

    josibake commented at 1:42 pm on June 20, 2023:
    Marking this as resolved for now, but keen to hear your feedback, if you have any @brandonblack
  7. in bip-0000.mediawiki:221 in f1c188faa5 outdated
    216+==== Selecting inputs ====
    217+
    218+The sending wallet performs coin selection as usual with the following restrictions:
    219+
    220+* At least one input MUST be from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    221+* Exclude ''P2TR'' script path spends, unless ''H'' is used as the internal public key
    


    brandonblack commented at 9:41 pm on June 6, 2023:

    Should this read

    Exclude ‘‘P2TR’’ outputs for whom ax * Bscan cannot be computed

    ?


    josibake commented at 2:35 pm on June 7, 2023:

    We want to be clear that the sender should never use the script path spend when funding a silent payment transaction. This is because a script path spend could contain any number of public keys and it becomes intractable for the receiver to try and determine which ones would have been used.

    In the event the sender uses a script path spend where the internal public keys is H, it is safe to include it to fund the transaction because the receiver can clearly skip it when collecting the public keys for the ECDH step.


    brandonblack commented at 4:18 pm on June 7, 2023:

    Hmm, can we instead put it on the recipient to exclude P2TR inputs from the tweak if they are spent with script path, but let the sender include them?

    Any exclusion of allowable inputs seems like a footgun.


    josibake commented at 4:28 pm on June 7, 2023:

    That would be ideal! Unfortunately, that causes a malleability issue with CoinJoin. This is explained in one of the footones:

    Why not skip all taproot script path spends? This causes malleability issues for CoinJoins. If the silent payments protocol skipped taproot script path spends, this would allow an attacker to join a CoinJoin round, participate in deriving the silent payment address using the tweaked private key for a key path spend, and then broadcast their own version of the transaction using the script path spend. If the receiver were to only consider key path spends, they would skip the attacker’s script path spend input when deriving the shared secret and not be able to find the funds.


    brandonblack commented at 7:31 pm on June 7, 2023:

    I see, but can’t we still include the script path spend as long as the keypath ax * Bscan is included in the tweak?

    So the rule would then be:

    Exclude “P2TR” inputs for which ax * Bscan (ax being the tweaked private key corresponding to the taproot output key) cannot be computed, unless they are spent using a script path and the internal public key is exactly equal to H.

    And the receiver would always include P2TR output keys even if they were spent using script path, unless the internal key is exactly H.

    There wouldn’t be many cases where this would be different in practice than what you wrote, however one can imagine a CKBunker-type device which would be willing to provide an answer to the question “What is your key times this public point” but wouldn’t be willing to sign a specific transaction spending its inputs. Then the spender could collect whatever other spending conditions might let them spend from the script path and still send the silent payment.


    RubenSomsen commented at 9:46 pm on June 7, 2023:

    one can imagine a CKBunker-type device which would be willing to provide an answer to the question “What is your key times this public point” but wouldn’t be willing to sign a specific transaction spending its inputs. Then the spender could collect whatever other spending conditions might let them spend from the script path and still send the silent payment.

    The specification as currently described already allows for this. Was there any part that gave you the impression that it didn’t?


    brandonblack commented at 10:02 pm on June 7, 2023:

    The specification as currently described already allows for this. Was there any part that gave you the impression that it didn’t?

    Exclude ''P2TR" script path spends <– that phrase in the input selection

    The requirement is that even if I am going to spend with script path I must include the taproot outputkey in the ECDH to prevent malleability.


    josibake commented at 4:30 am on June 8, 2023:

    a CKBunker-type device which would be willing to provide an answer to the question “What is your key times this public point” but wouldn’t be willing to sign a specific transaction spending its inputs. Then the spender could collect whatever other spending conditions might let them spend from the script path and still send the silent payment.

    If I’m understanding your example, the key path private key is on the CKBunker and never signs transactions, but the user can spend from a separate wallet using script path conditions for the same input(s)?


    brandonblack commented at 4:43 am on June 8, 2023:

    If I’m understanding your example, the key path private key is on the CKBunker and never signs transactions, but the user can spend from a separate wallet using script path conditions for the same input(s)?

    Yeah. I might have my CKBunker at home, set to sign nothing, but produce the ECDH shares for silent payments. I also have an OP_CSV <pubkey> OP_CHECKSIG script path for a key I keep on my phone 1 million sats per week, so I can send using my phone and silent payments up to that amount as long as my CKBunker is willing to provide its ECDH shares.

    Obviously I made up this example on the spot, but with vaults and other advanced constructions, I don’t think it makes sense to exclude the possibility that someone could have limited access to the keypath secret key (or shares), but want to spend using a script path for one reason or another.


    josibake commented at 4:54 am on June 8, 2023:

    Thanks for the explanation! Initially, my thinking on this was there’s never a (likely) scenario where you would have access to the taproot output key but choose to spend with the script path, so we could likely simplify implementations for the sender by just skipping inputs where we only have the script path data altogether in coin selection.

    But, given your example, I agree that there might be interesting use cases that we don’t want to rule out.


    RubenSomsen commented at 7:58 am on June 8, 2023:
    Perhaps as a foot note to point out there are theoretical exceptions to Exclude ''P2TR" script path spends. In practice almost none of the sender implementations are going to need to consider this, but it’s true, so worth mentioning.

    josibake commented at 9:22 am on June 8, 2023:
    I updated the wording to remove the “exclude” language, more as a placeholder because I think this still needs a bit of re-working (along with the “P2TR” section in “Inputs for Shared Secret Derivation”
  8. in bip-0000.mediawiki:200 in f1c188faa5 outdated
    189+
    190+''' P2TR (key path spends)'''
    191+
    192+The sender MUST use the tweaked private key corresponding to the taproot output key (i.e. the tweaked private key for a key path spend). If the taproot output key is not available, the output cannot be used for sending a silent payment. The receiver always uses the taproot output key when scanning, regardless of whether the taproot output is being used in a key path spend or a script path spend<ref name="why_always_output_pubkey">''' Why not skip all taproot script path spends? ''' This causes malleability issues for CoinJoins. If the silent payments protocol skipped taproot script path spends, this would allow an attacker to join a CoinJoin round, participate in deriving the silent payment address using the tweaked private key for a key path spend, and then broadcast their own version of the transaction using the script path spend. If the receiver were to only consider key path spends, they would skip the attacker's script path spend input when deriving the shared secret and not be able to find the funds.</ref>.
    193+
    194+The one exception is script path spends that use NUMS point ''H'' as their internal key (as defined in [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs BIP341: Constructing and spending Taproot outputs]), in which case the output will be skipped for the purposes of shared secret derivation<ref name="why_ignore_h">'''Why skip outputs with H as the internal taproot key?''' If use cases get popularized where the taproot key path cannot be used, these outputs can still be included without getting in the way of making a silent payment, provided they specifically use H as their internal taproot key.</ref>.
    


    brandonblack commented at 9:44 pm on June 6, 2023:

    Given that BIP-0341 recommends:

    In order to avoid leaking the information that key path spending is not possible it is recommended to pick a fresh integer r in the range 0…n-1 uniformly at random and use H + rG as internal key.

    It seems inadvisable to specify H directly in this BIP.


    josibake commented at 2:45 pm on June 7, 2023:

    There are certain use cases today where only the script path will be used (e.g. inscriptions), where it would be fine (and desirable) to use H as the internal public key. Seeing H clearly indicates to the recipient that this input was not used when deriving the silent payment address and allows them to skip the input when doing the shared secret derivation. Using H also allows the sender to fund silent payment transactions with UTXOs that were only ever intended to be spent as a script path spend.

    Also, IIUC, the fact that the key path spend was never possible is only revealed at spending time, which for use cases like inscriptions seems totally fine.


    brandonblack commented at 4:22 pm on June 7, 2023:
    I’m led to the same direction as on the exclusion of P2TR script path spends - rather than excluding or including inputs based on their construction, can we include or exclude inputs based on how they are spent? If a P2TR input is spent using its keypath then the spender can compute ax * Bscan, so that input MUST be included in the silent payment tweak. If a P2TR input is spent using a script path the recipient cannot know whether the spender could compute ax * Bscan so that input MUST NOT be included from the silent payment tweak.

    josibake commented at 4:31 pm on June 7, 2023:
    Doing it this way introduces a malleability issue with CoinJoins, see #1458 (review)
  9. brandonblack commented at 9:49 pm on June 6, 2023: contributor
    Glad to see this proposal advancing! I think with some minor changes it can be compatible with MuSig2 Taproot Keypaths (and potentially other off chain key aggregation schemes), which make it a powerful way of transacting privately even between users with advanced keys and scripts on Taproot.
  10. in bip-0000.mediawiki:176 in f1c188faa5 outdated
    171+=== Outpoints hash ===
    172+
    173+The sender and receiver MUST calculate an outpoints hash for the transaction in the following manner:
    174+
    175+* Collect each ''outpoint'' used as an input to the transaction
    176+* Let ''outpoints = outpoint<sub>0</sub> || … || outpoint<sub>n</sub>'', sorted by txid and vout, ascending order
    


    vostrnad commented at 8:23 pm on June 7, 2023:
    Why do the outpoints need sorting if they already have an order defined by the transaction?

    RubenSomsen commented at 9:58 pm on June 7, 2023:
    This makes it so the order of the inputs doesn’t matter for the resulting address, which is slightly safer even if issues around this are unlikely to occur. For instance, a wallet might RBF bump a tx and for some reason decide the change the order of the inputs, and neglect to realize this also means they’d have to recalculate the SP output. Now they won’t have to.

    josibake commented at 4:35 am on June 8, 2023:

    order defined by the transaction

    In addition to Ruben’s comment, I’d add I don’t think this order is defined until the transaction is signed, whereas you are generating the silent payment output before signing. Seems much safer to have an ordering defined by the protocol rather than requiring wallet software to ensure the prevout outpoints are always kept in the same order during the entire transaction creation process


    vostrnad commented at 4:47 pm on June 8, 2023:

    Thank you both for comments. In case you’d like to turn them into a footnote, it could be something like:

    Why are outpoints sorted before hashing? This way the silent payment output does not need to be recalculated if the wallet changes the order of inputs, e.g. at signing time or during a RBF bump.


    josibake commented at 10:36 am on June 14, 2023:
    Thanks for the suggested footnote! I’ve added it.
  11. in bip-0000.mediawiki:218 in f1c188faa5 outdated
    207+
    208+
    209+    scriptSig: <Signature> <Public Key>
    210+
    211+
    212+The receiver obtains the public key from the ''scriptSig''. The receiver MUST parse the ''scriptSig'' for the public key, even if the ''scriptSig'' is non-standard (e.g. <code><dummy> OP_DROP <Signature> <Public Key></code>). This is to address the [https://en.bitcoin.it/wiki/Transaction_malleability third-party malleability of ''P2PKH'' ''scriptSigs''].
    


    vostrnad commented at 8:24 pm on June 7, 2023:
    How exactly is the parsing of public keys from non-standard scriptSigs supposed to be done? The only foolproof way I can think of—executing the scriptSig and then reading the top stack element—requires a Script interpreter, which is a non-trivial requirement if you aren’t Bitcoin Core. If this is the case, it should probably be clarified.

    RubenSomsen commented at 10:11 pm on June 7, 2023:

    Yes, the logic behind this is that there are only two ways to scan for silent payments:

    1. Run a full node (in which case you’d have a script interpreter)
    2. Receive tweak data from a full node (the full node will have interpreted the script for you in order to generate the tweak data)

    So to the best of our knowledge there is no scenario where you’re not relying on a full node anyway, either directly or indirectly.

    That said, it seems a full script interpreter would not even be strictly required in this case. All you’d need to do is to find the 33 or 65 byte stack elements in the scriptsig (the only two possibilities for (un)compressed keys) and check which one of those matches up to the hashed value in the scriptpubkey.

  12. in bip-0000.mediawiki:268 in f1c188faa5 outdated
    263+** Let ''outputs_to_check = all unspent taproot outputs in the transaction''
    264+** Starting with ''n = 0'':
    265+*** Let ''t<sub>n</sub> = sha256(ecdh_shared_secret || ser<sub>32</sub>(n))''
    266+*** Compute ''P<sub>n</sub> = t<sub>n</sub>·G + B<sub>spend</sub>''
    267+**** If ''P<sub>n</sub>'' is in ''outputs_to_check'', add it to the wallet and continue with ''n++''
    268+**** If ''P<sub>n</sub>'' is not found and the wallet has precomputed labels<ref name="precompute_labels">''' Why precompute labels?''' Naively, a wallet could some max integer ''M'' which indicates the total number of labels it has used. When checking a transaction, the wallet would need to add all possible labels to each output. This ends up being ''n·m'' additions, where ''n'' is the number of outputs in the transaction and ''m'' is the number of labels in the wallet</ref>:
    


    vostrnad commented at 8:24 pm on June 7, 2023:
    Missing word in “a wallet could some max integer”.

  13. vostrnad commented at 8:38 pm on June 7, 2023: contributor

    Some light review so far, but I’m really liking the proposal. Being able to easily implement just the sending part will no doubt greatly accelerate adoption by wallets.

    Apart from the inline comments I also have a few style nits:

    • “e.g” is used instead of “e.g.” in a few places.
    • Mixed usage of curly () and straight (') single quotes.
    • Some footnotes (“Rationale and References”) are missing a period at the end.
    • In Creating outputs, “Let a = a0 + a1 + … ai, where each ai” should have an be the last member of the sum, like in the rest of the document. Same goes for the sum in Scanning where the last member should be An.
  14. josibake force-pushed on Jun 8, 2023
  15. josibake commented at 9:25 am on June 8, 2023: member

    Thanks for the review, @vostrnad !

    Apart from the inline comments I also have a few style nits:

    These should all be fixed now in https://github.com/bitcoin/bips/pull/1458/commits/1ae1b4bf80bc21911fbd90033edd8006d5e6b592

  16. in bip-0000.mediawiki:119 in 1ae1b4bf80 outdated
    114+Alice performs the tweak as before using one of the published ''(B<sub>scan</sub>, B<sub>m</sub>)'' pairs. Bob detects the labeled payment in the following manner:
    115+
    116+* Let ''P<sub>0</sub> = hash(outpoints_hash·b<sub>scan</sub>·A || 0)·G + B<sub>spend</sub>''
    117+* Subtract ''P<sub>0</sub>'' 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
    118+
    119+It is important to note that an outside observer can easily deduce that each published ''(B<sub>scan</sub>, B<sub>m</sub>)'' pair is owned by the same entity as each published address will have ''B<sub>scan</sub>'' 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.
    


    kallewoof commented at 3:47 am on June 12, 2023:

    This feels unnecessarily foot-shooty. Since m=[1,2,3,...] is arbitrary and only really important to Bob, why can’t he simply do m=[B<sub>scan</sub>*1, B<sub>scan</sub>*2, B<sub>scan</sub>*3,...] instead? No additional information is required, and Bob’s check would check for the above list, rather than the known-to-everybod int*G list.

    Edit: originally used privkeys for scan, but not sure if this would be potentially leaky so switched to (some specified serialization form of) the pubkey type. Not sure if that defeats the purpose though.


    josibake commented at 5:56 am on June 12, 2023:

    Hey @kallewoof, thanks for the review! With multiple Bscans, Bob now has to do the ECDH step multiple times for the same transaction to determine if there is an output for him. For someone with sufficiently powerful hardware, this might not be an issue, but it certainly wouldn’t be possible on mobile or even your “average” node runner using commodity hardware.

    Imagine the use case where Bob is an exchange and wants to have a unique SP address for each user: if he hands out a Bscan*int per user, he now has to do an EC mult per user, per transaction. With the labels approach we describe, he would only have to do one EC mult per transaction, subtract that from the outputs of the transaction and see if any of the remainders (int*G + Bspend ) are in his database with a simple lookup. This scales nicely to millions of users.

    rather than the known-to-everybod int*G list

    I’m not sure what you mean by known-to-everybody. If Bob posts two SP addresses, Bscan, Bspend + 1*G and Bscan, Bspend + 2*G, an outside observer can see two SP addresses with Bscan in common and infer that they are owned by the same entity, but they don’t know Bspend and can’t determine which int*G tweak was used, or even if an int*G tweak was used.


    kallewoof commented at 6:31 am on June 12, 2023:

    Hey @kallewoof, thanks for the review! With multiple Bscans, Bob now has to do the ECDH step multiple times for the same transaction to determine if there is an output for him. For someone with sufficiently powerful hardware, this might not be an issue, but it certainly wouldn’t be possible on mobile or even your “average” node runner using commodity hardware.

    Is powmod really that expensive? I thought it was pretty instant/cheap.

    Imagine the use case where Bob is an exchange and wants to have a unique SP address for each user: if he hands out a Bscan*int per user, he now has to do an EC mult per user, per transaction. With the labels approach we describe, he would only have to do one EC mult per transaction, subtract that from the outputs of the transaction and see if any of the remainders (int*G + Bspend ) are in his database with a simple lookup. This scales nicely to millions of users.

    If we’re talking millions of users, then perhaps it becomes prohibitively expensive, but otoh an exchange won’t be run off of a mobile device, so maybe they can handle that processing power bump. Perhaps I’m underestimating the complexity-bump here.

    I’m not sure what you mean by known-to-everybody. If Bob posts two SP addresses, Bscan, Bspend + 1*G and Bscan, Bspend + 2*G, an outside observer can see two SP addresses with Bscan in common and infer that they are owned by the same entity, but they don’t know Bspend and can’t determine which int*G tweak was used, or even if an int*G tweak was used.

    If I see a pubkey X, and I then see a pubkey X + (known to everyone) n*G where n is a tiny predictable number, then I can indeed determine which int*G tweak was used, no? It would require me to track a lot of data, so perhaps it’s no biggie, but it definitely isn’t impossible.


    josibake commented at 8:15 am on June 12, 2023:

    Is powmod really that expensive? I thought it was pretty instant/cheap

    It’s more that EC addition is cheaper. The other significant advantage in my mind is that if we apply the additive tweak to Bspend, we only have to do this once and can cache it. If we use the Bscan*int approach, we have to redo the calculation for each int for each new transaction (i.e Bscan*int*InputPubKeys)

    so maybe they can handle that processing power bump

    Perhaps worth mentioning there is nothing stopping an exchange (or anyone with sufficient processing power) from doing it this way and they would still be completely compatible with the SP protocol! The sender doesn’t care (or even know) how the receiver is tweaking their keys. Its not clear to me why the exchange would prefer this over the additive method, but they could do it. It would be a custom implementation, which would make recovering from backup into different wallet software an issue, but that’s more a concern for individual users, imo.

    If I see a pubkey X, and I then see a pubkey X + (known to everyone) n*G

    Right, my point was you’ve never seen pubkey X (Bspend, in this example), and you wouldn’t know it was the untweaked Bspend even if you saw it. All you see are three Pubkeys.

    I’d also add it doesn’t hurt the receiver’s privacy or security at all if you were to learn which int*G they used. It’s just a nice benefit that until you’ve seen multiple addresses with Bscan in common, you can’t know whether or not any particular SP addresses is using a label tweak or not.


    kallewoof commented at 8:45 am on June 12, 2023:

    I think I got caught on the initial "easily deduce that each published ''(B<sub>scan</sub>, B<sub>m</sub>)'' pair is owned by the same entity" part. But if I’m not mistaken, the Bspend pubkeys are not used directly, but they are tweaked in payments, so even knowing them I won’t know when a payment is made using them, unless I hold the corresponding privkey.

    Thanks, resolving.


    RubenSomsen commented at 8:55 am on June 12, 2023:

    Thanks for the comments @kallewoof.

    If I see a pubkey X, and I then see a pubkey X + (known to everyone) n*G where n is a tiny predictable number, then I can indeed determine which int*G tweak was used, no?

    The full calculation is P = hash(shared_secret)*G + X + n*G. Since outside observers don’t know hash(shared_secret)*G, they can’t figure out n.

    The recipient on the other hand can figure out n by calculating hash(shared_secret)*G + X and subtracting it from P (where P is every taproot output in a tx).

    Note that as soon as you start modifying shared_secret, you have to multiply by G again for every n. So scanning cost goes up linearly with n as opposed to staying the same no matter how big n is.

  17. in bip-0000.mediawiki:192 in 1ae1b4bf80 outdated
    187+
    188+Inputs with conditional branches or multiple public keys (e.g. ''CHECKMULTISIG'') are not included 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 that as described below.
    189+
    190+''' P2TR (key path spends)'''
    191+
    192+The sender MUST use the tweaked private key corresponding to the taproot output key (i.e. the tweaked private key for a key path spend). If the taproot output key is not available, the output cannot be used for sending a silent payment. The receiver always uses the taproot output key when scanning, regardless of whether the taproot output is being used in a key path spend or a script path spend<ref name="why_always_output_pubkey">''' Why not skip all taproot script path spends? ''' This causes malleability issues for CoinJoins. If the silent payments protocol skipped taproot script path spends, this would allow an attacker to join a CoinJoin round, participate in deriving the silent payment address using the tweaked private key for a key path spend, and then broadcast their own version of the transaction using the script path spend. If the receiver were to only consider key path spends, they would skip the attacker's script path spend input when deriving the shared secret and not be able to find the funds.</ref>.
    


    vostrnad commented at 9:21 pm on June 13, 2023:

    Using the term taproot output key to refer to both a public key and a private key is somewhat confusing. Additionally, that the key be tweaked is not required, only highly recommended. How about:

    0The sender MUST use the private key corresponding to the taproot output key (i.e. the tweaked private key for a key path spend). If this key is not available, the output cannot be used for sending a silent payment. The receiver always uses the taproot output key when scanning, regardless of whether the taproot output is being used in a key path spend or a script path spend<ref name="why_always_output_pubkey">''' Why not skip all taproot script path spends? ''' This causes malleability issues for CoinJoins. If the silent payments protocol skipped taproot script path spends, this would allow an attacker to join a CoinJoin round, participate in deriving the silent payment address using the tweaked private key for a key path spend, and then broadcast their own version of the transaction using the script path spend. If the receiver were to only consider key path spends, they would skip the attacker's script path spend input when deriving the shared secret and not be able to find the funds.</ref>.
    

    (If the diff isn’t rendering correctly for you, I removed the first instance of “tweaked” and replaced “the taproot output key” with “this key”.)


    josibake commented at 10:15 am on June 14, 2023:
    Good catch! I was incorrectly using “taproot output key” to refer to the private key, it should always refer to the public key. I’ve added your suggested wording.
  18. josibake force-pushed on Jun 14, 2023
  19. josibake force-pushed on Jun 14, 2023
  20. josibake force-pushed on Jun 14, 2023
  21. in bip-0000.mediawiki:151 in d9e5a1b61d outdated
    146+
    147+* Receiver addresses are always [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot outputs<ref name="why_taproot">'''Why only taproot outputs?''' Providing too much optionality for the protocol makes it difficult to implement and can be at odds with the goal of providing the best privacy. Limiting to taproot outputs helps simplify the implementation significantly while also putting users in the best eventual anonymity set.</ref>
    148+* The sender should sign with one of the sighash flags ''ALL, SINGLE, NONE'' (''ANYONECANPAY'' is unsafe). It is strongly recommended implementations only use ''SIGHASH_ALL'' for silent payments<ref name="why_sighash_all">'''Why recommend ''SIGHASH_ALL''?''' Since the output address for the receiver is derived from from the sum of the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' public keys, the inputs must not change once the sender has signed the transaction. If the inputs are allowed to change after the fact, the receiver will not be able to calculate the shared secret needed to find and spend the output. It is currently an open question on how a future version of silent payments could be made to work with new sighash flags such as ''SIGHASH_GROUP'' and ''SIGHASH_ANYPREVOUT''.</ref>
    149+* Inputs used to derive the shared secret are from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    150+
    151+A transaction is not a ''Silent Payments v0'' transaction and should be skipped entirely<ref name="why_skip_transactions">'''Why skip transactions that spend unknown output scripts?''' Skipping transactions that spend unknown output scripts allows us to have a clean upgrade path for Silent Payments by avoiding the need to scan the same transaction multiple times with different rule sets. If a fancy new output type is added in the future and Silent Payments v1 is released with support, we would want to avoid having to first scan the transaction with the silent payment v0 rules and then again with the silent payment v1 rules.</ref> when scanning if any of the following are true:
    


    vostrnad commented at 0:20 am on June 15, 2023:
    This footnote about new output types is specific only to the third bullet point below, so it should be moved there (and the ref name should probably be made more specific).
  22. in bip-0000.mediawiki:166 in d9e5a1b61d outdated
    161+* Let ''B<sub>scan</sub>, b<sub>scan</sub> = Receiver's [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] scan public key and corresponding private key''
    162+* Let ''B<sub>spend</sub>, b<sub>spend</sub> = Receiver's [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] spend public key and corresponding private key''
    163+* Let ''B<sub>m</sub> = B<sub>spend</sub> + m·G'', where ''m'' an optional integer tweak for labeling
    164+** In the case of ''m'' = 0, no label is applied and ''B<sub>m</sub> = B<sub>spend</sub>''
    165+* The final address is a [https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki Bech32m] encoding of:
    166+** The human-readable part "sp" for mainnet, "tsp" for testnets (e.g.  signet, testnet, regtest)
    


    vostrnad commented at 0:20 am on June 15, 2023:
    In the implementation PR the human-readable part for regtest is “sprt” but here it is “tsp”, which one is correct?

    josibake commented at 8:59 am on June 15, 2023:

    The implementation is out of date, will fix today.

    On this topic I’ve gotten feedback both ways: “regtest should be sprt to be consistent with bitcoin core” and “a special hrp for regtest is unnecessary.” I’m leaning towards keeping it as-is in the BIP and using one HRP for all test networks, but curious what your opinion on this is?


    josibake commented at 10:34 am on June 15, 2023:

    Did some digging on this and have come to the following conclusion:

    • regtest is a bitcoin core implementation specific concept. As such, I don’t think it should be specified in a BIP
    • its fine for bitcoin core to use the regtest HRP (for consistency with our own internal testing framework), but this could just as easily be swapped out for tsp or any other random string for regtest

    Based on this, I think its best to remove any reference to regtest from the BIP, and also keep regtest handling consistent with how other addresses are treated in bitcoin core.

    For more context, see: https://github.com/bitcoin/bitcoin/issues/12314


    vostrnad commented at 4:57 pm on June 15, 2023:
    I’m leaning slightly towards “sprt” for regtest, but I guess it doesn’t really need to be specified. BIP173 doesn’t specify the regtest HRP either.

    josibake commented at 7:30 am on June 16, 2023:
    I’ve removed regtest from the wording in the BIP, and I’ll leave the PR as is with sprt for regtest.
  23. in bip-0000.mediawiki:173 in d9e5a1b61d outdated
    164+** In the case of ''m'' = 0, no label is applied and ''B<sub>m</sub> = B<sub>spend</sub>''
    165+* The final address is a [https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki Bech32m] encoding of:
    166+** The human-readable part "sp" for mainnet, "tsp" for testnets (e.g.  signet, testnet, regtest)
    167+** The data-part values:
    168+*** The character "q", to represent a silent payment address of version 0
    169+*** The 64 byte concatenation of the receiver's public keys, ''B<sub>scan</sub> || B<sub>m</sub>''
    


    vostrnad commented at 0:21 am on June 15, 2023:
    BIP173 gives a limit of 90 characters for a Bech32 string, so the required increased limit of 115 should be mentioned here.

    josibake commented at 7:31 am on June 16, 2023:
    Done.
  24. in bip-0000.mediawiki:192 in d9e5a1b61d outdated
    187+
    188+Inputs with conditional branches or multiple public keys (e.g. ''CHECKMULTISIG'') are not included 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 that as described below.
    189+
    190+''' P2TR (key path)'''
    191+
    192+The sender MUST use the private key corresponding to the taproot output key (i.e. the tweaked private key for a key path spend). This can be a single private key or an aggregate key (e.g. taproot outputs using Musig2 or FROST)<ref name="musig_frost_support">'''Are key aggregation techniques like FROST and Musig2 supported?''' Any taproot output able to do a key path spend is supported. While a full specification of how to do this securely is outside the scope of this BIP, in theory any offline key aggregation technique can be used, such as FROST or Musig2. This would require participants to perform the ECDH step collaboratively e.g ''ECDH = a<sub>0</sub>·B<sub>scan</sub> + a<sub>1</sub>·B<sub>scan</sub> + ... + a<sub>t</sub>·B<sub>scan</sub>'' and ''P = hash(outpoints_hash·ECDH || 0)·G + B<sub>spend</sub>''. Additionally, it may be necessary for the participants to provide a DLEQ proof to ensure they are not acting maliciously.</ref>. If this key is not available, the output cannot be used for sending a silent payment. The receiver always uses the taproot output key when scanning, regardless of whether the taproot output is being used in a key path spend or a script path spend<ref name="why_always_output_pubkey">''' Why not skip all taproot script path spends? ''' This causes malleability issues for CoinJoins. If the silent payments protocol skipped taproot script path spends, this would allow an attacker to join a CoinJoin round, participate in deriving the silent payment address using the tweaked private key for a key path spend, and then broadcast their own version of the transaction using the script path spend. If the receiver were to only consider key path spends, they would skip the attacker's script path spend input when deriving the shared secret and not be able to find the funds. Additionally, there may be scenarios where a sender has access to the key path private key but spends the output using the script path.</ref>.
    


    vostrnad commented at 0:21 am on June 15, 2023:

    nits:

    • “e.g” instead of “e.g.”
    • “Musig2” instead of “MuSig2”
  25. in bip-0000.mediawiki:221 in d9e5a1b61d outdated
    216+==== Selecting inputs ====
    217+
    218+The sending wallet performs coin selection as usual with the following restrictions:
    219+
    220+* At least one input MUST be from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    221+* The sending wallet MUST have access to the taproot output key, unless ''H'' is used as the internal public key
    


    vostrnad commented at 0:21 am on June 15, 2023:

    Same as above, taproot output key only refers to the public key:

    0* For each taproot output spent the sending wallet MUST have access to the private key corresponding to the taproot output key, unless ''H'' is used as the internal public key
    
  26. in bip-0000.mediawiki:239 in d9e5a1b61d outdated
    234+*** Let ''P<sub>mn</sub> = t<sub>n</sub>·G + B<sub>m</sub>''
    235+*** Encode ''P<sub>mn</sub>'' as a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot output
    236+*** Optionally, repeat with n++ to create additional outputs for the current ''B<sub>m</sub>''
    237+*** If no additional outputs are required, continue to the next ''B<sub>m</sub>'' with ''n++''
    238+
    239+The sender should check to ensure that no two outputs in the final transaction have the same ''t<sub>n</sub>'' as this could result in a loss of privacy for the receiver<ref name="why_not_the_same_tn">''' Why can't two outputs have the same ''t<sub>n</sub>''?''' If paying the same entity but to two separate labeled addresses in the same transaction without incrementing ''n'', the two outputs would be ''t<sub>n</sub>·G + B<sub>spend</sub> + i·G'' and ''t<sub>n</sub>·G + B<sub>spend</sub> + j·G''. The attacker could subtract the two values and observe that the distance between i and j is small. This would allow them to deduce that this transaction is a silent payment transaction and that a single entity received two outputs, but won't tell them who the entity is.</ref>.
    


    vostrnad commented at 0:22 am on June 15, 2023:
    How could two outputs have the same tn if the algorithm above is implemented correctly? What exactly is meant by “check to ensure”?

    josibake commented at 9:02 am on June 15, 2023:
    There is no way this could happen if the sender is following the protocol. IIRC, we added this sentence as a belt and suspenders, but I’m inclined to remove it and instead make sure we have a test case for this.
  27. in bip-0000.mediawiki:251 in d9e5a1b61d outdated
    246+
    247+A scan and spend key pair using BIP32 derivation are defined (taking inspiration from [https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki BIP44]) in the following manner:
    248+
    249+
    250+    Spend private key: m / purpose' / coin_type' / account' / silent_payment_version' / 0' / 0
    251+     Scan private key: m / purpose' / coin_type' / account' / silent_payment_version' / 1' / 0
    


    vostrnad commented at 0:22 am on June 15, 2023:
    Why does the silent payment version need to be in the derivation path? It seems to me that a future version would be its own BIP and therefore purpose field.

    josibake commented at 9:13 am on June 15, 2023:

    This is here precisely because I don’t see us needing a new BIP for new versions of silent payments. Imagine we get a new output type and want to add it to the “Inputs for shared secret derivation” list. All we’d need to do is update this BIP with a version 1 section and everything else would remain unchanged.

    However, the more I think about this, maybe having the version in the derivation path is a bad idea. Imagine the same scenario I described above: for this upgrade, it would be great if you could keep the same secret data and backups, and just update your address with a new version number (sp1p...). This sp v1 address could still accept payments from v0 wallets, as well as v1 wallets.

    If there is a different upgrade to silent payments which fundamentally changes how the address is used (e.g using something besides x-only pubkeys), then we would likely have a new BIP number, which makes the version field unnecessary.

    Thoughts @RubenSomsen ?


    josibake commented at 11:01 am on June 16, 2023:

    The more I’ve thought about this, I think we should remove the version from the derivation path since having it would require users to generate new keys when there is likely no reason to.

    I’ve updated the BIP to reflect this and also added a blurb to the version section.

  28. vostrnad commented at 0:25 am on June 15, 2023: contributor

    Second round of review. To complement this I wrote a quick and dirty implementation covering most of both sending and receiving. It was quite easy and seems to work, can’t wait for test vectors to see how many bugs it has! 😅

    In addition to the inline comments I have one general comment and one style nit:

    • There are no specifics on how to deal with situations where EC operations fail to produce a valid point. I’m not sure if this is necessary, just mentioning this because my implementation has a lot of assertions.
    • Tweak expressions are not consistent in whether the G part comes first or second. For example, in BIP341 it always comes second.
  29. Khademr7 approved
  30. josibake force-pushed on Jun 15, 2023
  31. josibake force-pushed on Jun 15, 2023
  32. josibake force-pushed on Jun 15, 2023
  33. josibake commented at 10:44 am on June 15, 2023: member

    Thanks for the continued review, @vostrnad !

    There are no specifics on how to deal with situations where EC operations fail to produce a valid point. I’m not sure if this is necessary, just mentioning this because my implementation has a lot of assertions.

    Do you have a specific scenario in mind? I’m certainly not an expert in this area, but I don’t think we should get a failure by doing additions and multiplications (summing priv keys, pub keys, ecdh). If we can have a failure when summing up the private keys, public keys, or during the ECDH step, our only recourse would be to restart the coin selection process and ensure we get different inputs.

  34. josibake force-pushed on Jun 15, 2023
  35. josibake force-pushed on Jun 15, 2023
  36. vostrnad commented at 5:00 pm on June 15, 2023: contributor

    I don’t think we should get a failure by doing additions and multiplications (summing priv keys, pub keys, ecdh)

    One reason EC operations can fail is when a pseudorandom scalar value exceeds the curve order, which should only happen with negligible probability but other BIPs still have special cases for when it happens (e.g. BIP32, BIP340 and BIP341). I suppose it could be fine to ignore this, but I’m certainly no expert either.

  37. in bip-0000.mediawiki:171 in d342de6e48 outdated
    166+** The human-readable part "sp" for mainnet, "tsp" for testnets (e.g.  signet, testnet)
    167+** The data-part values:
    168+*** The character "q", to represent a silent payment address of version 0
    169+*** The 64 byte concatenation of the receiver's public keys, ''B<sub>scan</sub> || B<sub>m</sub>''
    170+
    171+Note: [https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki BIP173] imposes a 90 character for bech32 strings, whereas a silent payment address requires 115 characters.
    


    vostrnad commented at 5:10 pm on June 15, 2023:
    0Note: [https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki BIP173] imposes a 90 character limit for Bech32 strings, whereas a silent payment address requires 115 characters.
    
  38. in bip-0000.mediawiki:235 in d342de6e48 outdated
    230+* Group receiver silent payment addresses by ''B<sub>scan</sub>'' (e.g. each group consists of one ''B<sub>scan</sub>'' and one or more ''B<sub>m</sub>'')
    231+* For each group:
    232+** Let ''ecdh_shared_secret = outpoints_hash·a·B<sub>scan</sub>'', where ''ecdh_shared_secret'' is a compressed public key
    233+** Let ''n = 0''
    234+** For each ''B<sub>m</sub>'' in the group:
    235+*** Let ''t<sub>n</sub> = sha256(ecdh_shared_secret || ser<sub>32</sub>(n))''
    


    vostrnad commented at 5:10 pm on June 15, 2023:
    The serialization of ecdh_shared_secret is implicit here. Perhaps it would be a good idea to reuse the function serP(P) from BIP32 and make it explicit? This also applies to other places where EC points are implicitly converted to byte arrays.

    josibake commented at 7:53 am on June 16, 2023:
    Already ran into an issue with this where serializing ecdh_shared_secret as x-only vs compressed gave different results :sweat_smile: .

    josibake commented at 10:30 am on June 16, 2023:
    Added! This definitely improves the readability
  39. in bip-0000.mediawiki:273 in d342de6e48 outdated
    255+==== Scanning ====
    256+
    257+For each transaction the receiving wallet suspects might be a silent payment to themselves, it must:
    258+
    259+* Generate the ''outpoints_hash'', using the method described above
    260+* Let ''A = A<sub>0</sub> + A<sub>1</sub> + … A<sub>n</sub>'', where each ''A<sub>i</sub>'' is the public key of an input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    


    vostrnad commented at 5:11 pm on June 15, 2023:
    In my own implementation I’m running into parity issues: because Bscan is a BIP340 public key which always has an even parity while A is a sum of potentially non-BIP340 keys and can thus have any parity, in about half the cases ecdh_shared_secret is different for sender and receiver. Isn’t there a step missing that corrects for this, or am I missing something?

    josibake commented at 7:45 am on June 16, 2023:
    Interesting, do you have some test data I can use to try and reproduce this? I think we have a test for this (mixing of taproot and non-taproot in the inputs) in the functional tests on the PR, so seems there is a step we are doing in the Bitcoin Core implementation that isn’t explained clearly in the BIP.

    vostrnad commented at 9:48 pm on June 16, 2023:

    Here are the test vectors that cause a parity mismatch in my implementation, along with the computed shared secret and partial computations.

    Bscan = 76e246e6b92aec2efd6c8db8037d284651ccd3c9b9ac1404010b7b3268016e92 bscan = ad8af2bd1bf108f3dd2ac74a2a5ec51d9a173c684c9e6365c6af93d5c5fdf61a Bspend = Bm = 8810c8029bb9560ab5c54241a108912f847ab7975df626f816430a64a35be68c A0 = A = 03603ce64dcf386cd11c97b6c6d94de719c4638101ae9613f2f1e6fae78bb89e0f a0 = a = 5f230aed01b015c4fdd78d209f878cb67be5135504b4d7a2c85b368d1c042b9d outpoints_hash = 2f11ade1789049b1a26837d517bae661fb3b2dfc00a0d812f65ee207c7617c05

    Sender: a·Bscan = 03bc2412cc4aad38e93f0ce1ad5628ae231aac7cf1b6f54dfb074999bd34076f68 ecdh_shared_secret = 028408231298cc86c22e61ca8ba1967dd9192d68bcb4ca575e7f8284ae9c417828

    Receiver: bscan·A = 02bc2412cc4aad38e93f0ce1ad5628ae231aac7cf1b6f54dfb074999bd34076f68 ecdh_shared_secret = 038408231298cc86c22e61ca8ba1967dd9192d68bcb4ca575e7f8284ae9c417828


    josibake commented at 11:26 am on June 19, 2023:

    Thanks for the test data! Your parity mismatch is coming from the fact that bscan produces an odd y value and needs to be negated.

    It was my understanding that in order to be a valid BIP340 public key, the private key must be negated if it returns an odd y. Rereading BIP340, however, this doesn’t seem to be clearly specified. The closest reference I can find is:

    Note that we use a very different public key format (32 bytes) than the ones used by existing systems (which typically use elliptic curve points as public keys, or 33-byte or 65-byte encodings of them). A side effect is that PubKey(sk) = PubKey(bytes(n - int(sk)), so every public key has two corresponding secret keys.

    So I think we do need to specify this in the silent payments BIP. Perhaps something like:

    “if has_even_y(Bscan) is false, negate bscan


    vostrnad commented at 6:49 pm on June 20, 2023:
    I don’t have a strong opinion on how to correct the parity mismatch, but “if has_even_y(Bscan) is false, negate bscan” has the same problem as mentioned below that Bscan is not an EC point but a byte array. BIP340: Default Signing includes notation for private key negation which you could reuse. Another option would be to use 33-byte keys instead of x-only keys (I suppose this choice was made to make the silent payment address shorter?).

    josibake commented at 10:27 am on June 21, 2023:

    I need to rework the notation a bit, per your point about byte arrays vs EC points. I think that can be fixed, though.

    Regarding the choice of x-only public keys: yes, one reason is saving 2 bytes in the silent payment address. The address is already quite long and might become longer in the future with new silent payment versions, so we want to be as conservative as possible. The second reason is that the spend key can be a FROST or MuSig2 aggregate public key, both of which are only defined for BIP340 keys, IIUC. That doesn’t stop us from adding and removing the extra byte as needed, but that seems clunky if we could just use x-only public keys instead.


    josibake commented at 11:19 am on June 27, 2023:

    I wrote some test cases for this and turns out it’s very difficult to keep track of the parity between sender and receiver, especially when using labels. While it could be done with x-only public keys, it would involve checking the evenness and negating keys at almost every step. I think the extra byte is definitely worth adding to keep things simple and save on computation when scanning.

    I’ve updated the specification to use 33-byte keys for the address, which I believe also addresses the notation concern.


    josibake commented at 11:24 am on June 27, 2023:
    Additionally, your example made me notice that we need to check both output - Pn and output_negated - Pn when scanning for labels. This wasn’t necessary when using the old method of brute-forcing the label checks for all m, which is why we didn’t notice it when we updated to use the subtraction technique when scanning for labels. I also updated the BIP to include this step. Thanks so much for working on an implementation and helping uncover this with your test case @vostrnad !

    vostrnad commented at 5:05 pm on June 29, 2023:
    I’ve looked over the changes and they seem to mostly address the parity issues, however it seems to me that one step is missing: doesn’t the sender need to negate the taproot private keys whose public keys have an odd Y coordinate?

    josibake commented at 6:14 pm on June 29, 2023:
    Yep, that’s correct. I had always assumed that being a taproot outpoint meant you always used the private key which corresponds with the even Y coordinate, but seems we should explicitly spell that out as a step in the BIP.

    josibake commented at 7:01 am on July 10, 2023:
    Added a sentence to mention checking and negating taproot keys, if necessary
  40. in bip-0000.mediawiki:139 in d342de6e48 outdated
    134+== Specification ==
    135+
    136+We use the following functions and conventions:
    137+
    138+* ''outpoint'' (36 bytes): the <code>COutPoint</code> of an input (32-byte hash + 4-byte little-endian)
    139+* ser<sub>32</sub>(i): serializes a 32-bit unsigned integer ''i'' as a 4-byte little-endian
    


    vostrnad commented at 5:11 pm on June 15, 2023:
    In BIP32, ser32(i) is a big-endian serialization. Since we’re reusing the notation here, shouldn’t we also reuse the meaning?

    josibake commented at 7:52 am on June 16, 2023:
    I’d prefer to keep it little-endian, as it’s more consistent with the rest of the BIP and consistent with how Bitcoin Core serializes uint32_t types. Perhaps naming the function ser_le to make it clear? Or dropping the function and just specifying that output_index is 4-byte little-endian?

    josibake commented at 10:33 am on June 16, 2023:

    On further reflection, I agree it’s better to reuse notation and conventions from other BIP. It’s also very likely that most wallets implementing silent payments will already have code written (or use libraries) with these serialization functions written for working with BIP32. Much better if we can reuse existing code rather than have wallets write custom code just for serializing data for silent payments.

    I’ve updated the spec to reflect this and will update the Bitcoin Core PR shortly.

  41. josibake force-pushed on Jun 16, 2023
  42. josibake commented at 11:09 am on June 16, 2023: member

    I don’t think we should get a failure by doing additions and multiplications (summing priv keys, pub keys, ecdh)

    One reason EC operations can fail is when a pseudorandom scalar value exceeds the curve order, which should only happen with negligible probability but other BIPs still have special cases for when it happens (e.g. BIP32, BIP340 and BIP341). I suppose it could be fine to ignore this, but I’m certainly no expert either.

    I’ll take a look to see how it’s being handled in those BIPs. Ideally, we can reuse the same solution here. The main thing is ensuring both the sender and receiver can deterministically handle these low-probability events.

  43. in bip-0000.mediawiki:167 in d920133138 outdated
    162+
    163+A silent payment address is constructed in the following manner:
    164+
    165+* Let ''B<sub>scan</sub>, b<sub>scan</sub> = Receiver's [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] scan public key and corresponding private key''
    166+* Let ''B<sub>spend</sub>, b<sub>spend</sub> = Receiver's [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] spend public key and corresponding private key''
    167+* Let ''B<sub>m</sub> = B<sub>spend</sub> + m·G'', where ''m'' an optional integer tweak for labeling
    


    vostrnad commented at 10:47 pm on June 16, 2023:

    BIP340 public keys are byte arrays, not EC points, so I believe this and all other expressions that implicitly treat them as such are technically wrong:

    Note that we use a very different public key format (32 bytes) than the ones used by existing systems (which typically use elliptic curve points as public keys, or 33-byte or 65-byte encodings of them).


    josibake commented at 11:25 am on June 27, 2023:
    I’ve updated the BIP to use 33-byte compressed keys, per https://github.com/bitcoin/bips/pull/1458/files#r1243572575, so I think this is all good now.
  44. kallewoof commented at 0:49 am on June 20, 2023: member
    @luke-jr Unless you see anything objectionable, please assign BIP number.
  45. josibake commented at 1:51 pm on June 20, 2023: member

    @vostrnad

    One reason EC operations can fail is when a pseudorandom scalar value exceeds the curve order, which should only happen with negligible probability but other BIPs still have special cases for when it happens (e.g. BIP32, BIP340 and BIP341). I suppose it could be fine to ignore this, but I’m certainly no expert either.

    I did some more reading on this and from what I can tell this applies to choosing a scalar which results in a valid point on the curve, which is not true for every scalar. In our case, we are already using existing valid private keys or, in the case of the silent payment address, are using BIP32 to generate them. Adding these keys together (using the elliptic curve group operation) or multiplying a valid scalar with a point (ECDH) gives us a result that is guaranteed to be a valid point on the curve.

  46. josibake commented at 2:23 pm on June 22, 2023: member

    we are already using existing valid private keys @vostrnad Actually, what I said above is not true and your point stands. When we do tn = hash(ecdh_shared_secret || n) and then multiply by G, we might get an invalid point, so we should include a step on how to handle this. Will update.

  47. josibake force-pushed on Jun 27, 2023
  48. josibake force-pushed on Jun 27, 2023
  49. luke-jr renamed this:
    BIP for Silent Payments
    BIP 352: Silent Payments
    on Jun 29, 2023
  50. luke-jr commented at 4:37 pm on June 29, 2023: member
    Assigned BIP number 352
  51. luke-jr added the label New BIP on Jun 29, 2023
  52. josibake force-pushed on Jul 3, 2023
  53. josibake force-pushed on Jul 3, 2023
  54. josibake force-pushed on Jul 3, 2023
  55. josibake force-pushed on Jul 3, 2023
  56. josibake commented at 5:28 pm on July 3, 2023: member
    I’ve added a reference Python implementation along with test vectors for sending and receiving. I’ll be updating the “Appendix B: Test Vectors” section shortly.
  57. josibake force-pushed on Jul 4, 2023
  58. josibake force-pushed on Jul 4, 2023
  59. josibake force-pushed on Jul 4, 2023
  60. josibake force-pushed on Jul 4, 2023
  61. josibake force-pushed on Jul 6, 2023
  62. josibake force-pushed on Jul 6, 2023
  63. josibake force-pushed on Jul 6, 2023
  64. josibake force-pushed on Jul 6, 2023
  65. josibake force-pushed on Jul 6, 2023
  66. josibake force-pushed on Jul 6, 2023
  67. josibake force-pushed on Jul 6, 2023
  68. josibake force-pushed on Jul 6, 2023
  69. in bip-0352.mediawiki:91 in dc0b30711b outdated
    86+
    87+Bob must include the same ''outpoint_hash'' when scanning.
    88+
    89+''' Using all inputs '''
    90+
    91+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<ref name="other_inputs">'''What about inputs without public keys?''' Inputs without public keys can still be spent in the transaction but are simply ignored in the ''Silent Payments'' protocol.</ref>. This significantly reduces Bob's scanning requirement, makes light client support more feasible<ref name="using_all_inputs">'''How does using all inputs help light clients?''' If Alice uses a random input for the tweak, Bob necessarily has to have access to and check all transaction inputs, which requires performing an ECC multiplication per input. If instead Alice performs the tweak with the sum of the input public keys, Bob only needs the summed 32 byte public key per transaction and only does one ECC multiplication per transaction. Bob can then use BIP158 block filters to determine if any of the outputs exist in a block and thus avoids downloading transactions which don't belong to him. It is still an open question as to how Bob can source the 32 bytes per transaction in a trustless manner, see [[#appendix-a-light-client-support|Appendix A: Light Client Support]] for more details.</ref>, 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 [https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406 Blind-Diffie-Hellman] to prevent the other participants from learning who Alice is paying.</ref>.
    


    RubenSomsen commented at 12:07 pm on July 6, 2023:
    Should be 33 bytes
  70. in bip-0352.mediawiki:117 in dc0b30711b outdated
    112+* Publish ''(B<sub>scan</sub>, B<sub>0</sub>)'', ''(B<sub>scan</sub>, B<sub>1</sub>) …''
    113+
    114+Alice performs the tweak as before using one of the published ''(B<sub>scan</sub>, B<sub>m</sub>)'' pairs. Bob detects the labeled payment in the following manner:
    115+
    116+* Let ''P<sub>0</sub> = B<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A || 0)·G''
    117+* Subtract ''P<sub>0</sub>'' 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
    


    RubenSomsen commented at 12:32 pm on July 6, 2023:

    Could use a footnote, something like:

    0Alternatively, ''m·G'' could be added to ''P<sub>0</sub>'' for each label ''m'' and compared to the transaction outputs. This is more performant in cases where the number of eligible outputs exceeds the number of labels in use. In practice this will mainly apply to users that choose never to use labels.
    

    Or maybe this could be added to footnote 17 “Why precompute the labels?”

  71. in bip-0352.mediawiki:180 in dc0b30711b outdated
    175+|}
    176+
    177+''v31'' (l) is reserved for a backwards incompatible change, if needed. For ''Silent Payments v0'':
    178+
    179+* If the receiver's silent payment address version is:
    180+** ''v0'': check that the data part is exactly 66-bytes. Otherwise, fail
    


    RubenSomsen commented at 12:40 pm on July 6, 2023:
    I can imagine people will end up wanting to embed optional metadata in the address, such as their pseudonym. The way it’s written now, we’d force them to use v1 for that…

    josibake commented at 6:25 am on July 10, 2023:

    For me, this is a feature. If people are going to include metadata, I’d argue it needs to be formally defined so wallets can actually use it. Writing it this way ensures there is a strict definition of what a v0 address is, but in a way that is forwards compatible with people adding metadata in a v1, v2, etc.

    But I don’t feel super strongly about it; do you see any downsides to requiring a new version for adding metadata?

  72. in bip-0352.mediawiki:191 in dc0b30711b outdated
    186+
    187+A transaction is not a ''Silent Payments v0'' transaction and should be skipped entirely when scanning if any of the following are true:
    188+
    189+* The transaction does not contain any BIP341 taproot outputs
    190+* The transaction does not have at least one input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    191+* The transaction inputs contain a new, undefined output type (e.g. SegWit versions > 1)<ref name="skip_txs_with_unknown_prevouts">'''Why skip transactions that spend unknown output scripts?''' Skipping transactions that spend unknown output scripts allows us to have a clean upgrade path for Silent Payments by avoiding the need to scan the same transaction multiple times with different rule sets. If a fancy new output type is added in the future and Silent Payments v1 is released with support, we would want to avoid having to first scan the transaction with the silent payment v0 rules and then again with the silent payment v1 rules.</ref>
    


    RubenSomsen commented at 1:12 pm on July 6, 2023:
    We probably need to be more precise in our language here to ensure we covered all bases. Any ambiguity here will cause issues. What about the nVersion field inside transactions? I’m not fully sure at the moment how they relate to soft forks.

    josibake commented at 2:42 pm on July 10, 2023:
    From what I’ve heard, the general idea is that soft forks via transaction nVersion is deprecated in favor of repurposing OP_SUCCESS, using a new witness version, or using a new tapscript leaf version. Not saying it won’t ever be used, so we could say v0 skips transactions with nVersion > 3, but we’d need a new silent payments version if it turns out nVersion=4 is used for something other than a soft fork. Could also be ultra-conservative and say skip anything > 2, but that almost guarantees we’d need to add a new silent payment version if the transaction v3 proposal keeps making progress.

    josibake commented at 3:49 pm on July 10, 2023:
    I went ahead and added it as nVersion < 4, so that we don’t need to update for transaction relay.

    josibake commented at 8:28 am on August 3, 2023:
    Circling back, I ended up removing the nVersion restriction after a few offline conversations regarding how this field is meant to be used. TLDR; I think it’s safe to assume nVersion will not be used for signaling a soft fork, and thus only be used to communicate policy changes.
  73. in bip-0352.mediawiki:232 in dc0b30711b outdated
    227+
    228+''' P2TR '''
    229+
    230+The sender MUST use the private key corresponding to the taproot output key (i.e. the tweaked private key for a key path spend). This can be a single private key or an aggregate key (e.g. taproot outputs using MuSig2 or FROST)<ref name="musig_frost_support">'''Are key aggregation techniques like FROST and MuSig2 supported?''' Any taproot output able to do a key path spend is supported. While a full specification of how to do this securely is outside the scope of this BIP, in theory any offline key aggregation technique can be used, such as FROST or MuSig2. This would require participants to perform the ECDH step collaboratively e.g. ''ECDH = a<sub>0</sub>·B<sub>scan</sub> + a<sub>1</sub>·B<sub>scan</sub> + ... + a<sub>t</sub>·B<sub>scan</sub>'' and ''P = B<sub>spend</sub> + hash(outpoints_hash·ECDH || 0)·G''. Additionally, it may be necessary for the participants to provide a DLEQ proof to ensure they are not acting maliciously.</ref>. If this key is not available, the output cannot be included as an input to the transaction. The receiver always uses the taproot output key when scanning, regardless of whether the taproot output is using a key path spend or a script path spend<ref name="why_always_output_pubkey">''' Why not skip all taproot script path spends? ''' This causes malleability issues for CoinJoins. If the silent payments protocol skipped taproot script path spends, this would allow an attacker to join a CoinJoin round, participate in deriving the silent payment address using the tweaked private key for a key path spend, and then broadcast their own version of the transaction using the script path spend. If the receiver were to only consider key path spends, they would skip the attacker's script path spend input when deriving the shared secret and not be able to find the funds. Additionally, there may be scenarios where a sender has access to the key path private key but spends the output using the script path.</ref>.
    231+
    232+The one exception is script path spends that use NUMS point ''H'' as their internal key (as defined in [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs BIP341: Constructing and spending Taproot outputs]), in which case the output will be skipped for the purposes of shared secret derivation<ref name="why_ignore_h">'''Why skip outputs with H as the internal taproot key?''' If use cases get popularized where the taproot key path cannot be used, these outputs can still be included without getting in the way of making a silent payment, provided they specifically use H as their internal taproot key.</ref>.
    


    RubenSomsen commented at 1:18 pm on July 6, 2023:
    Maybe we should just copy the sentence in question from BIP341 instead of merely referring to it. It’s easier for readers and that way our BIP does not depend on whether BIP341 remains unchanged.
  74. in bip-0352.mediawiki:296 in dc0b30711b outdated
    253+
    254+==== Selecting inputs ====
    255+
    256+The sending wallet performs coin selection as usual with the following restrictions:
    257+
    258+* At least one input MUST be from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    


    RubenSomsen commented at 1:21 pm on July 6, 2023:

    Another restriction is that none of the inputs must exceed segwit v1 (and depending on the answer to my previous comment we may also need to put restrictions on the nVersion field?)

    I think ideally we should refer back here to the part where it says “A transaction is not a Silent Payments v0 transaction and should be skipped entirely when scanning if any of the following are true”


    josibake commented at 3:49 pm on July 10, 2023:
    Added it here explicitly, but also linked to the new section
  75. in bip-0352.mediawiki:305 in dc0b30711b outdated
    300+
    301+Wallet software MUST use hardened derivation to ensure the master key is not exposed in the event the scan private key is compromised. Purpose is a constant set to ''352'' following the BIP43 recommendation. Refer to [https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki BIP43] and [https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki BIP44] for more details.
    302+
    303+==== Scanning ====
    304+
    305+For each transaction the receiving wallet suspects might be a silent payment to themselves, it must:
    


    RubenSomsen commented at 1:32 pm on July 6, 2023:
    More precisely, any tx that is an eligible SP tx should be scanned. This should also refer back to “A transaction is not a Silent Payments v0 transaction and should be skipped entirely when scanning if any of the following are true”
  76. in bip-0352.mediawiki:319 in dc0b30711b outdated
    314+*** Compute ''P<sub>n</sub> = B<sub>spend</sub> + t<sub>n</sub>·G''
    315+*** For each ''output'' in ''outputs_to_check'':
    316+**** If ''P<sub>n</sub>'' equals ''output'':
    317+***** Add ''P<sub>n</sub>'' to the wallet
    318+***** Remove ''output'' from ''outputs_to_check'' and rescan ''outputs_to_check'' with ''n++''
    319+**** Else, if the wallet has precomputed labels (including the change label, if used)<ref name="precompute_labels">''' Why precompute labels?''' Naively, a wallet could store some max integer ''M'' which indicates the total number of labels it has used. When checking a transaction, the wallet would need to add all possible labels to each output. This ends up being ''n·m'' additions, where ''n'' is the number of outputs in the transaction and ''m'' is the number of labels in the wallet. By precomputing the labels, the wallet only needs to compute ''m·G'' once per label and can determine if a label was used via a lookup, rather than adding each label to each output.</ref>:
    


    RubenSomsen commented at 1:41 pm on July 6, 2023:

    I think the wording needs to change to say change labels must be scanned.

    Ideally we’d have two recommendations: a. No labels except for the change label b. X number of labels (where X is some number that seems reasonable pending benchmarks)

    If the user knows they never used labels then they can scan with option a, if unsure they’ll have to scan for b, if they went beyond X they’ll have to manually pick a high enough number.


    josibake commented at 11:58 am on August 3, 2023:
    This is only relevant when re-scanning, so I think we should mention something there. It doesn’t really make sense to me for a new wallet to start scanning and always scan for change and labels when it knows for a fact it does not use them.

    josibake commented at 11:30 am on November 1, 2023:
    updated, per your edits
  77. in bip-0352.mediawiki:435 in dc0b30711b outdated
    388+
    389+Below is a list of functional tests which should be included in sending and receiving implementations.
    390+
    391+==== Sending ====
    392+
    393+* Ensure taproot outputs are excluded during coin selection if the sender does not have access to the key path private key (unless using ''H'' as the taproot internal key)
    


    RubenSomsen commented at 1:47 pm on July 6, 2023:
    Same as previous comments, this should exclude things that would cause the tx to be skipped for SP, like segwit v>1 and refer back to “A transaction is not a Silent Payments v0 transaction and should be skipped entirely when scanning if any of the following are true”
  78. in bip-0352.mediawiki:187 in dc0b30711b outdated
    182+** ''v31'': fail
    183+* Receiver addresses are always [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot outputs<ref name="why_taproot">'''Why only taproot outputs?''' Providing too much optionality for the protocol makes it difficult to implement and can be at odds with the goal of providing the best privacy. Limiting to taproot outputs helps simplify the implementation significantly while also putting users in the best eventual anonymity set.</ref>
    184+* The sender should sign with one of the sighash flags ''ALL, SINGLE, NONE'' (''ANYONECANPAY'' is unsafe). It is strongly recommended implementations only use ''SIGHASH_ALL'' for silent payments<ref name="why_sighash_all">'''Why recommend ''SIGHASH_ALL''?''' Since the output address for the receiver is derived from from the sum of the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' public keys, the inputs must not change once the sender has signed the transaction. If the inputs are allowed to change after the fact, the receiver will not be able to calculate the shared secret needed to find and spend the output. It is currently an open question on how a future version of silent payments could be made to work with new sighash flags such as ''SIGHASH_GROUP'' and ''SIGHASH_ANYPREVOUT''.</ref>
    185+* Inputs used to derive the shared secret are from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    186+
    187+A transaction is not a ''Silent Payments v0'' transaction and should be skipped entirely when scanning if any of the following are true:
    


    RubenSomsen commented at 1:53 pm on July 6, 2023:
    This is actually a pretty important section, which should probably have its own header. See later comments.

    josibake commented at 3:50 pm on July 10, 2023:
    I re-wrote this as its own section, feel free to add more or propose changes. I found that it read better once I reframed it as “conditions you need to check TO scan,” vs “conditions for not scanning”
  79. RubenSomsen dismissed
  80. RubenSomsen commented at 2:00 pm on July 6, 2023: contributor
    We’ve been polishing this like crazy, but still noticed one important issue: we’ll need to be more explicit about when a tx isn’t SP eligible with regards to soft forks and actively take this into account during transaction creation.
  81. josibake force-pushed on Jul 10, 2023
  82. josibake force-pushed on Jul 10, 2023
  83. josibake force-pushed on Jul 10, 2023
  84. josibake force-pushed on Jul 10, 2023
  85. josibake force-pushed on Jul 10, 2023
  86. josibake commented at 9:41 am on July 11, 2023: member

    There was a suggestion to use BIP21 for silent payments instead of introducing a new address format, the reasoning being introducing yet another address format is a burden on wallet developers. I looked into this a bit, and here’s my thinking:

    Case 1: bitcoin:bc1pspendpublickey?sp=0x02scanpublickey

    The “address” is the spend public key encoded as a taproot address and an sp= extension is added which contains the scan public key. Wallets that understand the sp= extension make a silent payment using both keys, wallets that don’t skip the extension and send the funds to the spend public key. You can argue that this is no worse than people posting a static address today, with the option to receive funds more privately if the sender understands silent payments. I disagree for the following reasons:

    • Encourages address reuse: a receiver who did not previously have a static address posted will now post a static address as a fallback to receive silent payments. Encouraging users to post a static address is an anti-pattern, as increases the amount of address reuse, which is exactly what we want to avoid
    • Damages the privacy of users who do support sending silent payments: Bob receives a payment from Alice, where Alice’s wallet doesn’t understand the sp= extension, so she sends it to his spend public key. Everyone can see that Bob received a payment to his silent payment address. Carol sends a payment to Bob as a silent payment. Bob then spends the UTXO from Alice and the UTXO from Carol together, effectively linking Carol’s transaction to Bob’s silent payment address.

    Case 2: bitcoin:bc1pxxx?sp=0x02spendpublickey0x03scanpublickey

    The address is a static bitcoin address and the sp= param contains the spend and scan public key. The same critiques of case 1 apply in this case, as well.

    Case 3: bitcoin:bc1pxxx?r-sp=0x02spendpublickey0x03scanpublickey

    By adding r- to the param, we make the extension required. This is the same as introducing a new address format: either wallets support sending to it, or the payment fails. I don’t see any benefits of using BIP21 over introducing a new address in this case.

    Proposed solution

    Keep the sp1qxxx bech32m encoding and add it to BIP21 once there is widespread support for sending to silent payment addresses and something like BOLT12. This would allow us to construct a static payment code URI like bitcoin:sp1qxxx?lightning=lno1pg257enxv4ezqcneype82um50ynhxgrwdajx283qfwdpl28qqmc78ymlvhmxcsywdk5wrjnj36jryg488qwlrnzyjczs, where the wallet first uses the bolt12 offer and falls back to a silent payment address. While I agree that introducing a new address format isn’t ideal, I don’t see a way to introduce a reusable payment code protocol without specifying a new address type. This was also mentioned in the design of BIP173. Reusing bech32m does a lot to alleviate the address format fatigue in that any wallet wanting to send to a silent payment address needs to be able to create taproot outputs, so they will already have bech32m encoding and decoding logic. We’ve also made the silent payment address forward compatible with new silent payment versions, so ideally this format is the one-and-done reusable payment code format.

    cc @TheBlueMatt, in case I’ve missed something or misrepresented the arguments.

  87. in bip-0352.mediawiki:345 in 3530d9837b outdated
    302@@ -303,6 +303,70 @@ If using a seed phrase only style backup, the user can recover the wallet's unsp
    303 
    304 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.
    305 
    306+== Test Vectors ==
    307+
    308+A [[bip-0340/test-vectors.csv|collection of test vectors in JSON format]] are provided, along with a [[bip-0340/reference.py|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. Test cases use the following schema:
    


    Sosthene00 commented at 10:49 pm on July 17, 2023:
    I think links are wrong here :)

    josibake commented at 7:30 am on July 18, 2023:
    Thanks! That is indeed the wrong link, will update!
  88. in bip-0352.mediawiki:211 in cfe0771a04 outdated
    207+** The human-readable part "sp" for mainnet, "tsp" for testnets (e.g.  signet, testnet)
    208+** The data-part values:
    209+*** The character "q", to represent a silent payment address of version 0
    210+*** The 66 byte concatenation of the receiver's public keys, ''ser<sub>P</sub>(B<sub>scan</sub>) || ser<sub>P</sub>(B<sub>m</sub>)''
    211+
    212+Note: [https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki BIP173] imposes a 90 character limit for Bech32 strings, whereas a silent payment address requires 117 characters.
    


    theStack commented at 3:59 pm on July 22, 2023:

    I was wondering how exactly we end up with a limit of 117 characters, maybe it’s worth it to include a short breakdown (e.g. as a foot note)? IIUC, this should be

    • HRP [2-3 characters]
    • separator [1 character]
    • version [1 character]
    • payload, 66 bytes concatenated pubkeys [ ceil(66*8/5) = 106 characters]
    • checksum [6 characters]

    i.e. silent payment addresses have a fixed size of 117 characters on testnet/signet/regtest and 116 characters on mainnet.


    josibake commented at 8:47 am on August 3, 2023:

    I’ve updated this to recommend raising the character limit to 1023 for silent payment addresses. This is because new versions of silent payments may add additional bytes to the data field. For forward compatibility, the specification says a silent payments v0 sender can send to v0 - v30 addresses - they just read the first 66 bytes and discard the rest. This doesn’t work if there is a size limit on the address since a v0 sender would fail to decode a v1 - v30 address.

    Also, raising the limit seems “safe” per BIP173 and IIUC this is what lightning is using for their invoices.


    josibake commented at 9:00 am on August 3, 2023:
    Also, yes, it would be nice to add a breakdown to the footnotes! Thanks for providing one!
  89. in bip-0352.mediawiki:204 in cfe0771a04 outdated
    199+
    200+A silent payment address is constructed in the following manner:
    201+
    202+* Let ''B<sub>scan</sub>, b<sub>scan</sub> = Receiver's scan public key and corresponding private key''
    203+* Let ''B<sub>spend</sub>, b<sub>spend</sub> = Receiver's spend public key and corresponding private key''
    204+* Let ''B<sub>m</sub> = B<sub>spend</sub> + m·G'', where ''m'' an optional integer tweak for labeling
    


    Sosthene00 commented at 9:08 am on July 25, 2023:
    label or m is defined as an integer or an int, which I suppose means it is supposed to be 4 bytes long. Since we need to convert it to a 32 bytes scalar to tweak the spend key wouldn’t it be clearer and easier to specify it explicitly as such? I think it would also allow using private keys derived on another adjacent path (say m/352'/0'/0'/3', but that’s just an idea) directly as labels, which could be nice for back up / recovery too. Another way would be to just leave labels out of the specification and say this is whatever you can turn into a 256 bits scalar that can tweak a key, and the specific way to obtain that is implementation specific (maybe some services would rather have human readable labels and hash it to obtain the silent payment label)

    josibake commented at 11:45 am on July 27, 2023:

    Thanks for the review @Sosthene00 ! I think you’re right that we need to specify this. Defining the label tweak as a 256 bit scalar seems the best choice, since this label m ends up as part of the private key when labels are used.

    The reason for specifying it as an incremental integer is to easily recover your wallet from backup without any extra data about how many labels you used or derived them. The idea is you would pick some very large number M when scanning and check all integer values under that. But ultimately, it is up to the receiving client how to implement and I can imagine use cases where the label is an integer with some other meaning (like a product number or invoice number). I think maybe the best approach is like you said: specify that the label integer must be a 256-bit number, and we recommend making it an incremental scheme to enable easy backups.

    Regarding the BIP32 idea, I’m unsure if it’s possible to easily check if a public key exists in a given derivation path, without traversing it, right? So you’d still need a datastore to do the efficient “subtract and lookup” method when scanning.


    Sosthene00 commented at 10:23 am on July 31, 2023:
    Yes I was making the assumption that the receiver maintains some kind of labels database to check against incoming candidate transactions. What you mean is that if we use a simple incremental scheme for labels we don’t even need a database to find out outputs that belong to us (beside easy backup)? You still need some kind of data storage to match labels to some identity and/or context though, but even if you lose it you won’t lose any of your money so that’s still something I guess.
  90. in bip-0352.mediawiki:295 in cfe0771a04 outdated
    279+** For each ''B<sub>m</sub>'' in the group:
    280+*** Let ''t<sub>n</sub> = sha256(ser<sub>P</sub>(ecdh_shared_secret) || ser<sub>32</sub>(n))''
    281+*** Let ''P<sub>mn</sub> = B<sub>m</sub> + t<sub>n</sub>·G''
    282+*** Encode ''P<sub>mn</sub>'' as a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot output
    283+*** Optionally, repeat with n++ to create additional outputs for the current ''B<sub>m</sub>''
    284+*** If no additional outputs are required, continue to the next ''B<sub>m</sub>'' with ''n++''<ref name="why_not_the_same_tn">''' Why not re-use ''t<sub>n</sub>''? when paying different labels to the same receiver?''' If paying the same entity but to two separate labeled addresses in the same transaction without incrementing ''n'', the two outputs would be ''B<sub>spend</sub> + t<sub>n</sub>·G + i·G'' and ''B<sub>spend</sub> + t<sub>n</sub>·G + j·G''. The attacker could subtract the two values and observe that the distance between i and j is small. This would allow them to deduce that this transaction is a silent payment transaction and that a single entity received two outputs, but won't tell them who the entity is.</ref>
    


    Sosthene00 commented at 5:45 pm on July 26, 2023:
    Thinking about it if labels are not small incremental int as I suggested above we may reuse tn. Not sure if it makes a lot of difference in practice though, I don’t think it will often happen that a sender wants to pay that many labels of the same receiver in one transaction so that not reusing tn would induce a noticable burden.

    josibake commented at 11:52 am on July 27, 2023:
    That’s a good point, however, the sender has no idea what kind of label scheme the receiver has used, so it’s possible their transaction could be doxxed as a silent payment transaction if the receiver uses an incremental integer scheme. Also worth pointing out that hashing the integer counter for each new output is a relatively cheap operation, and doing so gives us a guarantee that the transaction won’t leak information, regardless of the labeling scheme used by the receiver

    Sosthene00 commented at 10:13 am on July 31, 2023:
    Agree, probably best as it is now.
  91. in bip-0352.mediawiki:308 in cfe0771a04 outdated
    270+
    271+* Collect the private keys for each input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    272+* For each private key ''a<sub>i</sub>'' corresponding to a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot output, check that the private key produces a point with an even y-value and negate the private key if not<ref name="why_negate_taproot_private_keys">'''Why do taproot private keys need to be checked?''' Recall from [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] that each x-only public key has two corresponding private keys, ''d'' and ''n - d''. To maintain parity between sender and receiver, it is necessary to use the private key corresponding to the even y-value when performing the ECDH step since the receiver will assume the even y-value when summing the taproot x-only public keys.</ref>
    273+* Let ''a = a<sub>0</sub> + a<sub>1</sub> + … a<sub>n</sub>'', where each ''a<sub>i</sub>'' has been negated if necessary
    274+* Generate the ''outpoints_hash'', using the method described above
    275+* Group receiver silent payment addresses by ''B<sub>scan</sub>'' (e.g. each group consists of one ''B<sub>scan</sub>'' and one or more ''B<sub>m</sub>'')
    


    Sosthene00 commented at 11:02 am on July 31, 2023:

    I think the specification should define the sorting of the _m_s in each _scan_ group. In the reference implementation it just appends the m keys in the order it finds it, meaning that altering the order of the inputs will result in different keys being generated.

    If this is intended it means that the caller is responsible for the order of the arguments, which is asking for trouble I think (I can’t see how the receiver could know for sure the order of the labels the sender used). Otoh it’s only a problem when sender wants to pay multiple labels of the same receiver in one transaction, which is arguably rare, but still.

    See also https://github.com/cygnet3/rust-silentpayments/issues/11


    josibake commented at 11:53 am on July 31, 2023:

    You are correct that changing the ordering will change the outputs created, but this isn’t a problem for the receiver: the receiver does not need to know the ordering of the outputs to be able to find them.

    I can see how this would be annoying from a testing perspective, however, because changing the order of the Bm values will lead to completely different output sets if multiple labels are being paid by the same sender. I’ll add a sorting step, but also a footnote that mentions that sorting is not necessary for the protocol but useful for determinism in testing.


    Sosthene00 commented at 12:47 pm on July 31, 2023:

    After rereading carefully the bip, I see how the receiver would not be affected by this.

    Actually sorting the labels as I first thought is indeed impossible, as the sender can’t be aware of them. It could sort m_pubkeys though, but maybe it doesn’t worth it.

    If it’s only a problem with testing, I think adding it inside the specification might be more confusing than really useful, it’s probably better to add it as a comment in the reference implementation, so that future implementer won’t get confused like I did.

    Or maybe only in a sidenote here, basically saying : “while the sender modifying the order of ms would result in different keys being generated, that doesn’t affect the ability of the recipient to find them since it only looks at the difference between his B_spend and the actual key in an output, to check if this difference match a label.


    josibake commented at 12:50 pm on July 31, 2023:

    If it’s only a problem with testing, I think adding it inside the specification might be more confusing than really useful, it’s probably better to add it as a comment in the reference implementation, so that future implementer won’t get confused like I did.

    Agreed. I’ll add an explanation to the testing section of the BIP, along with a comment in the reference implementation.


    josibake commented at 8:30 am on August 3, 2023:
    Added a comment to the reference implementation and mentioned this in the testing section
  92. josibake force-pushed on Jul 31, 2023
  93. josibake force-pushed on Aug 1, 2023
  94. josibake force-pushed on Aug 1, 2023
  95. josibake force-pushed on Aug 1, 2023
  96. josibake force-pushed on Aug 1, 2023
  97. in bip-0352.mediawiki:185 in 64da95202f outdated
    180+* If the receiver's silent payment address version is:
    181+** ''v0'': check that the data part is exactly 66-bytes. Otherwise, fail
    182+** ''v1'' through ''v30'': read the first 66-bytes of the data part and discard the remaining bytes (if any)
    183+** ''v31'': fail
    184+* Receiver addresses are always [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot outputs<ref name="why_taproot">'''Why only taproot outputs?''' Providing too much optionality for the protocol makes it difficult to implement and can be at odds with the goal of providing the best privacy. Limiting to taproot outputs helps simplify the implementation significantly while also putting users in the best eventual anonymity set.</ref>
    185+* The sender should sign with one of the sighash flags ''ALL, SINGLE, NONE'' (''ANYONECANPAY'' is unsafe). It is strongly recommended implementations only use ''SIGHASH_ALL'' for silent payments<ref name="why_sighash_all">'''Why recommend ''SIGHASH_ALL''?''' Since the output address for the receiver is derived from from the sum of the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' public keys, the inputs must not change once the sender has signed the transaction. If the inputs are allowed to change after the fact, the receiver will not be able to calculate the shared secret needed to find and spend the output. It is currently an open question on how a future version of silent payments could be made to work with new sighash flags such as ''SIGHASH_GROUP'' and ''SIGHASH_ANYPREVOUT''.</ref>
    


    murchandamus commented at 2:52 pm on August 2, 2023:
    This could be read as recommending that P2TR inputs explicitly use SIGHASH_ALL, while hopefully SIGHASH_DEFAULT will be more common. Perhaps you may want to explicitly mention that SIGHASH_DEFAULT is preferred where applicable, and otherwise SIGHASH_ALL should be used.

    josibake commented at 8:48 am on August 3, 2023:
    Great point! I’ll update this
  98. in bip-0352.mediawiki:194 in 64da95202f outdated
    189+
    190+A transaction is a ''Silent Payments v0'' transaction and MUST be scanned if and only if all of the following are true:
    191+
    192+* The transaction contains at least one BIP341 taproot output
    193+* The transaction has at least one input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    194+* The transaction does not contain a new, undefined output type (e.g. SegWit versions > 1)<ref name="skip_txs_with_unknown_prevouts">'''Why skip transactions that spend unknown output scripts?''' Skipping transactions that spend unknown output scripts allows us to have a clean upgrade path for Silent Payments by avoiding the need to scan the same transaction multiple times with different rule sets. If a fancy new output type is added in the future and Silent Payments v1 is released with support, we would want to avoid having to first scan the transaction with the silent payment v0 rules and then again with the silent payment v1 rules.</ref>
    


    murchandamus commented at 2:57 pm on August 2, 2023:
    This is ambiguous. I first thought it refers to an output, but after reading the footnote, I surmise that only an input spending a new output type would be an issue. Could you clarify whether an output with an unknown type would be an issue?

    josibake commented at 8:49 am on August 3, 2023:
    Great catch, this is referring to inputs that spend unknown output types. Having an unknown output type in the outputs is perfectly fine. I’ll update this to be explicit that we are referring to inputs here.
  99. in bip-0352.mediawiki:295 in 64da95202f outdated
    281+*** Let ''t<sub>n</sub> = sha256(ser<sub>P</sub>(ecdh_shared_secret) || ser<sub>32</sub>(n))''
    282+*** Let ''P<sub>mn</sub> = B<sub>m</sub> + t<sub>n</sub>·G''
    283+*** Encode ''P<sub>mn</sub>'' as a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot output
    284+*** Optionally, repeat with n++ to create additional outputs for the current ''B<sub>m</sub>''
    285+*** If no additional outputs are required, continue to the next ''B<sub>m</sub>'' with ''n++''<ref name="why_not_the_same_tn">''' Why not re-use ''t<sub>n</sub>''? when paying different labels to the same receiver?''' If paying the same entity but to two separate labeled addresses in the same transaction without incrementing ''n'', the two outputs would be ''B<sub>spend</sub> + t<sub>n</sub>·G + i·G'' and ''B<sub>spend</sub> + t<sub>n</sub>·G + j·G''. The attacker could subtract the two values and observe that the distance between i and j is small. This would allow them to deduce that this transaction is a silent payment transaction and that a single entity received two outputs, but won't tell them who the entity is.</ref>
    286+** Optionally, if the sending wallet implements receiving silent payments, it can create change outputs in the following manner:
    


    murchandamus commented at 3:13 pm on August 2, 2023:
    If the recommendation is that Silent Payment users use BIP32 to derive scan and spend key, why would they not use BIP32 to derive change addresses but opt into creating change outputs from their scan key? It’s not clear to me what their motivation to do so would be.

    josibake commented at 8:58 am on August 3, 2023:

    If a wallet is only doing silent payments (i.e. doesn’t have other descriptors), then I’d argue it’s much simpler to use the silent payments protocol for change: you use BIP32 one time to get a spend and scan key and then the rest of the wallet code handles all UTXOs / rescanning / spending, etc as silent payment outputs. For a silent payments only wallet, this also means there are no gap limit concerns when re-scanning / importing from a backup.

    If a wallet already supports using BIP32 and other descriptors and they are ADDING silent payments, then I don’t really see an advantage of one over the other except that a user may want to keep change outputs created from paying a silent payment address separate from the rest of the change outputs in their wallet.


    RubenSomsen commented at 8:59 pm on August 6, 2023:

    Related to the gap limit, there is also the theoretical advantage that you can recover access to your funds from just the UTXO set + tweak data, something you’d be unable to do with BIP32 unless you brute force it with a huge gap limit.

    Wallets are free to handle the creation of change outputs in any way they like, but it is important that all wallets always scan for SP style change outputs to ensure no funds are overlooked when importing from a different wallet.

  100. josibake force-pushed on Aug 3, 2023
  101. josibake force-pushed on Aug 3, 2023
  102. josibake force-pushed on Aug 3, 2023
  103. josibake force-pushed on Aug 3, 2023
  104. josibake force-pushed on Aug 3, 2023
  105. in bip-0352.mediawiki:28 in 30f957d7e9 outdated
    23+
    24+=== Motivation ===
    25+
    26+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.
    27+
    28+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 <ref name="out_of_band_notifications">'''Why not use out-of-band notifications''' Out of band notifications (e.g. using something other than the Bitcoin blockchain) have been proposed as a way of addressing the privacy and cost concerns of using the Bitcoin blockchain as a messaging layer. This, however, simply moves the privacy and cost concerns somewhere else and increases the risk of losing money due to a notification not being reliably delivered, or even censored, and makes this notification data critical for backup to recover funds.</ref>. 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.
    


    vostrnad commented at 9:50 pm on August 3, 2023:

    Extra whitespace before footnote:

    0However, 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<ref name="out_of_band_notifications">'''Why not use out-of-band notifications''' Out of band notifications (e.g. using something other than the Bitcoin blockchain) have been proposed as a way of addressing the privacy and cost concerns of using the Bitcoin blockchain as a messaging layer. This, however, simply moves the privacy and cost concerns somewhere else and increases the risk of losing money due to a notification not being reliably delivered, or even censored, and makes this notification data critical for backup to recover funds.</ref>. 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.
    
  106. in bip-0352.mediawiki:60 in 30f957d7e9 outdated
    55+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:
    56+
    57+* Let ''P = B + hash(a·B)·G''
    58+* Encode ''P'' as a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot output
    59+
    60+Since ''a·B == b·A'' ([https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman 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 = B + hash(b·A)·G'' and seeing that ''P'' is present in the transaction outputs).
    


    vostrnad commented at 9:51 pm on August 3, 2023:
    0Since ''a·B == b·A'' ([https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman 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).
    
  107. in bip-0352.mediawiki:64 in 30f957d7e9 outdated
    59+
    60+Since ''a·B == b·A'' ([https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman 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 = B + hash(b·A)·G'' and seeing that ''P'' is present in the transaction outputs).
    61+
    62+''' Creating more than one output '''
    63+
    64+In order to allow Alice to create more than one output for Bob<ref name="why_more_than_one_output">'''Why allow for more than one output?''' Allowing Alice to break her payment to Bob into multiple amounts opens up a number of privacy improving techniques for Alice, making the transaction look like a CoinJoin or better hiding the change amount by splitting both the payment and change outputs into multiple amounts. It also allows for Alice and Carol to both have their own unique output paying Bob in the event they are in a collaborative transaction and both paying Bob's silent payment address.</ref>, we included an integer in the following manner:
    


    vostrnad commented at 9:51 pm on August 3, 2023:
    0In order to allow Alice to create more than one output for Bob<ref name="why_more_than_one_output">'''Why allow for more than one output?''' Allowing Alice to break her payment to Bob into multiple amounts opens up a number of privacy improving techniques for Alice, making the transaction look like a CoinJoin or better hiding the change amount by splitting both the payment and change outputs into multiple amounts. It also allows for Alice and Carol to both have their own unique output paying Bob in the event they are in a collaborative transaction and both paying Bob's silent payment address.</ref>, we include an integer in the following manner:
    
  108. in bip-0352.mediawiki:91 in 30f957d7e9 outdated
    86+
    87+Bob must include the same ''outpoint_hash'' when scanning.
    88+
    89+''' Using all inputs '''
    90+
    91+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<ref name="other_inputs">'''What about inputs without public keys?''' Inputs without public keys can still be spent in the transaction but are simply ignored in the ''Silent Payments'' protocol.</ref>. This significantly reduces Bob's scanning requirement, makes light client support more feasible<ref name="using_all_inputs">'''How does using all inputs help light clients?''' If Alice uses a random input for the tweak, Bob necessarily has to have access to and check all transaction inputs, which requires performing an ECC multiplication per input. If instead Alice performs the tweak with the sum of the input public keys, Bob only needs the summed 33 byte public key per transaction and only does one ECC multiplication per transaction. Bob can then use BIP158 block filters to determine if any of the outputs exist in a block and thus avoids downloading transactions which don't belong to him. It is still an open question as to how Bob can source the 32 bytes per transaction in a trustless manner, see [[#appendix-a-light-client-support|Appendix A: Light Client Support]] for more details.</ref>, 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 [https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406 Blind-Diffie-Hellman] to prevent the other participants from learning who Alice is paying.</ref>.
    


    vostrnad commented at 9:51 pm on August 3, 2023:

    Blind-Diffie-Hellman → Blind Diffie–Hellman

    0In 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<ref name="other_inputs">'''What about inputs without public keys?''' Inputs without public keys can still be spent in the transaction but are simply ignored in the ''Silent Payments'' protocol.</ref>. This significantly reduces Bob's scanning requirement, makes light client support more feasible<ref name="using_all_inputs">'''How does using all inputs help light clients?''' If Alice uses a random input for the tweak, Bob necessarily has to have access to and check all transaction inputs, which requires performing an ECC multiplication per input. If instead Alice performs the tweak with the sum of the input public keys, Bob only needs the summed 33 byte public key per transaction and only does one ECC multiplication per transaction. Bob can then use BIP158 block filters to determine if any of the outputs exist in a block and thus avoids downloading transactions which don't belong to him. It is still an open question as to how Bob can source the 32 bytes per transaction in a trustless manner, see [[#appendix-a-light-client-support|Appendix A: Light Client Support]] for more details.</ref>, 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 [https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406 Blind Diffie–Hellman] to prevent the other participants from learning who Alice is paying.</ref>.
    
  109. in bip-0352.mediawiki:108 in 30f957d7e9 outdated
     96+* Let ''a = a<sub>0</sub> + a<sub>1</sub> … + a<sub>n</sub>''
     97+* Let ''P<sub>0</sub> = B + hash(outpoints_hash·a·B || 0)·G''
     98+
     99+''' Spend and Scan Key '''
    100+
    101+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<sub>scan</sub>, B<sub>spend</sub>)''. This allows Bob to keep ''b<sub>spend</sub>'' in offline cold storage and perform the scanning with the public key ''B<sub>spend</sub>'' and private key ''b<sub>scan</sub>''. Alice performs the tweak using both of Bob's public keys in the following manner:
    


    vostrnad commented at 9:52 pm on August 3, 2023:

    There should be a footnote explaining the rationale for having an independent scan key, as opposed to deriving it from the spend key. You mentioned in the PR review club that one reason was redundancy (a compromised spend key doesn’t lead to loss of funds without also a compromised scan key), which I take some issue with1, and also something about labels not working without an independent scan key, which sounds better, but would be great to have an explanation for.

    1 This kind of silent redundancy (pun not intended) isn’t IMO something that should be purposely built into a protocol as it breaks the separation of responsibilities. The scan key is responsible for scanning for payments, and the consequence of its compromise is loss of privacy. The spend key is responsible for spending received outputs, and the consequence of its compromise should be understood as loss of funds, even if by a quirk of the protocol it doesn’t necessarily have to be. Redundancy can be built on top of the spend key by using MuSig2 or FROST, not by treating the scan key as more important than it is.

    Also the fact that both the spend and scan key are required for spending should perhaps be explicitly mentioned somewhere. Due to the naming one might think that the spend key is enough.


    josibake commented at 8:56 am on August 4, 2023:

    labels not working without an independent scan key

    To be precise, my point was that for labels to work, there is no way for the sender to infer the spend public key from the scan public key. The suggestion from the review club was if we can somehow derive the spend private key from the scan private key, there might be a way for the sender to derive the spend public key from the scan public key, allowing us to post one public key instead of two.

    Even if the spend key is derived from the scan key, labels work by adding another public key (m·G) to the spend public key, where m is unknown to the sender.

    This kind of silent redundancy (pun not intended) isn’t IMO something that should be purposely built into a protocol as it breaks the separation of responsibilities.

    You make a really good point. First, I agree we need to be more explicit: both the spend key and scan key are required to spend funds. The scan key is needed for finding the “shared secret” portion of the private key and the spend key is needed to ensure it is only spendable by the recipient. Given that, having independent derivation is a weak argument in that it only addresses one very narrow case: you compromise the spend key, but you don’t compromise your BIP32 master key.

    Originally, we had the scan key defined as the hash of the spend key, which then allows you to derive the spend key with or without BIP32. What I’m unsure of is what’s easier/safer for hardware wallets: hashing a spend key and exporting that, or exporting a private key from a specific derivation path. Depending on the answer to that question, I may be more inclined to go back to the original: scan_key = hash(spend_key)


    RubenSomsen commented at 8:49 pm on August 6, 2023:

    there might be a way for the sender to derive the spend public key from the scan public key, allowing us to post one public key instead of two

    That is impossible (unless scan key == spend key, but then that would defeat the purpose)


    josibake commented at 12:38 pm on November 1, 2023:
    @RubenSomsen I think we may still want a footnote here? Based on recent discussions, our rational for how it is defined now is simplicity and likely the most compatible across wallet implementations today. That being said, I do think we will need a wallet BIP in the future for silent payments, as this BIP should stay primarily focused on just the protocol.

    murchandamus commented at 2:26 pm on May 8, 2024:
    Is this still an open todo?

    josibake commented at 3:35 pm on May 8, 2024:
    I’d say no. There is a “Spending” section in the specification which specifies the full key needed for spending. I also think its better to define how to derive the spend and scan key in a separate BIP, rather than add more footnotes here.
  110. in bip-0352.mediawiki:117 in 30f957d7e9 outdated
    112+* Publish ''(B<sub>scan</sub>, B<sub>0</sub>)'', ''(B<sub>scan</sub>, B<sub>1</sub>) …''
    113+
    114+Alice performs the tweak as before using one of the published ''(B<sub>scan</sub>, B<sub>m</sub>)'' pairs. Bob detects the labeled payment in the following manner:
    115+
    116+* Let ''P<sub>0</sub> = B<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A || 0)·G''
    117+* Subtract ''P<sub>0</sub>'' 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
    


    vostrnad commented at 9:52 pm on August 3, 2023:
    0* Let ''B<sub>m</sub> = B<sub>spend</sub> + m·G''
    1* Publish ''(B<sub>scan</sub>, B<sub>0</sub>)'', ''(B<sub>scan</sub>, B<sub>1</sub>)'' etc.
    2
    3Alice performs the tweak as before using one of the published ''(B<sub>scan</sub>, B<sub>m</sub>)'' pairs. Bob detects the labeled payment in the following manner:
    4
    5* Let ''P<sub>0</sub> = B<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A || 0)·G''
    6* Subtract ''P<sub>0</sub>'' from each of the transaction outputs and check if the remainder matches any of the labels (''1·G'', 2·G'' etc.) that the wallet has previously used
    

    vostrnad commented at 3:52 pm on August 4, 2023:
    My bad, should be ''1·G'', ''2·G'' etc..
  111. in bip-0352.mediawiki:138 in 30f957d7e9 outdated
    133+
    134+== Specification ==
    135+
    136+We use the following functions and conventions:
    137+
    138+* ''outpoint'' (36 bytes): the <code>COutPoint</code> of an input (32-byte txid, least significant byte first || 4-byte vout, least significant byte first)<ref name="why_little_endian">'''Why are outpoints little-endian?''' Despite using big endian throughout the rest of the BIP, outpoints are sorted and hashed matching their transaction serialization, which is little-endian. This allows a wallet to parse a serialized transaction for use in silent payments without needing to re-order the bytes when compute the outpoint hash. Note: despite outpoints being stored and serialized as little-endian, the transaction hash (txid) is always displayed as big-endian.</ref>
    


    vostrnad commented at 9:53 pm on August 3, 2023:
    0* ''outpoint'' (36 bytes): the <code>COutPoint</code> of an input (32-byte txid, least significant byte first || 4-byte vout, least significant byte first)<ref name="why_little_endian">'''Why are outpoints little-endian?''' Despite using big endian throughout the rest of the BIP, outpoints are sorted and hashed matching their transaction serialization, which is little-endian. This allows a wallet to parse a serialized transaction for use in silent payments without needing to re-order the bytes when computing the outpoint hash. Note: despite outpoints being stored and serialized as little-endian, the transaction hash (txid) is always displayed as big-endian.</ref>
    
  112. in bip-0352.mediawiki:139 in 30f957d7e9 outdated
    134+== Specification ==
    135+
    136+We use the following functions and conventions:
    137+
    138+* ''outpoint'' (36 bytes): the <code>COutPoint</code> of an input (32-byte txid, least significant byte first || 4-byte vout, least significant byte first)<ref name="why_little_endian">'''Why are outpoints little-endian?''' Despite using big endian throughout the rest of the BIP, outpoints are sorted and hashed matching their transaction serialization, which is little-endian. This allows a wallet to parse a serialized transaction for use in silent payments without needing to re-order the bytes when compute the outpoint hash. Note: despite outpoints being stored and serialized as little-endian, the transaction hash (txid) is always displayed as big-endian.</ref>
    139+* sort<sub>outpoints</sub>(v): sorts a vector ''v'' of ''outpoints'' in ascending order by doing a byte by byte comparison lexicographically.
    


    vostrnad commented at 9:53 pm on August 3, 2023:

    sortoutpoints(v) is only used once, so it doesn’t make much sense to define a special function for it. Also, it confusingly sorts the vector in place instead of returning a new value like all the other functions. Lastly, what was wrong with the way it was worded in Outpoints hash? Seems completely fine to me:

    Let outpoints = outpoint0 || … || outpointn, sorted by txid and vout, ascending order


    josibake commented at 7:35 am on August 4, 2023:

    It was mentioned by another reviewer offline that how to sort the outpoints was a bit unclear, but I’m leaning toward taking it out and just mentioning lexicographic sorting here. Something like:

    Let outpoints = outpoint0 || … || outpointn, sorted lexicographically by txid and vout, ascending order

  113. in bip-0352.mediawiki:148 in 30f957d7e9 outdated
    143+
    144+For everything not defined above, we use the notation from [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#specification BIP340].
    145+
    146+=== Versions ===
    147+
    148+This document defines ''Silent Payments v0''. Version is communicated through the address in the same way as Segwit addresses. 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.
    


    vostrnad commented at 9:53 pm on August 3, 2023:
    The document is inconsistent in both the casing and italicization of “silent payments”. Unsure about the casing, but I’d definitely drop the italics.

    josibake commented at 9:23 am on August 4, 2023:
    A quick grammar refresher led me to believe lowercasing throughout is probably the best option.
  114. in bip-0352.mediawiki:182 in 30f957d7e9 outdated
    178+''v31'' (l) is reserved for a backwards incompatible change, if needed. For ''Silent Payments v0'':
    179+
    180+* If the receiver's silent payment address version is:
    181+** ''v0'': check that the data part is exactly 66-bytes. Otherwise, fail
    182+** ''v1'' through ''v30'': read the first 66-bytes of the data part and discard the remaining bytes (if any)
    183+** ''v31'': fail
    


    vostrnad commented at 9:53 pm on August 3, 2023:
    Some rationale for this would be nice. What kind of changes could be made in a new version without breaking compatibility with v0?

    josibake commented at 7:54 am on August 4, 2023:

    Here are a few (admittedly contrived) examples:

    • A new output type gets added to the “Inputs for shared secret derivation” list. This would be a new address version so that senders know they can include the new output type when sending to a v1 address, and v0 senders can also send to the v1 address using the v0 rules.
    • A new output type gets added and is meant to replace taproot, so now the v1 is interpreted as “create outputs as if possible, otherwise create taproot outputs”
    • Some solution for optimistic notifications develops and the receiver adds some metadata to the payload to indicate how they want to receive notifications (nostr, simplex, hand-wavy p2p messaging that doesn’t exist yet). A v1 sender would parse that extra metadata and use it, whereas a v0 sender would read the first 66 bytes and ignore the rest.
    • Some fancy new key type is added for quantum resistance and the key is added as a third public key to the payload. v1 knows to use the third key, whereas a v0 sender use the first 66 bytes and discards the rest

    josibake commented at 12:38 pm on November 1, 2023:
    @RubenSomsen would also be great if we could come up with a clean example for the BIP, either as an example or as a footnote.
  115. in bip-0352.mediawiki:185 in 30f957d7e9 outdated
    180+* If the receiver's silent payment address version is:
    181+** ''v0'': check that the data part is exactly 66-bytes. Otherwise, fail
    182+** ''v1'' through ''v30'': read the first 66-bytes of the data part and discard the remaining bytes (if any)
    183+** ''v31'': fail
    184+* Receiver addresses are always [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot outputs<ref name="why_taproot">'''Why only taproot outputs?''' Providing too much optionality for the protocol makes it difficult to implement and can be at odds with the goal of providing the best privacy. Limiting to taproot outputs helps simplify the implementation significantly while also putting users in the best eventual anonymity set.</ref>
    185+* The sender should sign with one of the sighash flags ''DEFAULT, ALL, SINGLE, NONE'' (''ANYONECANPAY'' is unsafe). It is strongly recommended implementations use ''SIGHASH_DEFAULT'' when applicable, or ''SIGHASH_ALL''<ref name="why_sighash_default_or_all">'''Why recommend ''SIGHASH_[DEFAULT|ALL]''?''' Since the output address for the receiver is derived from from the sum of the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' public keys, the inputs must not change once the sender has signed the transaction. If the inputs are allowed to change after the fact, the receiver will not be able to calculate the shared secret needed to find and spend the output. It is currently an open question on how a future version of silent payments could be made to work with new sighash flags such as ''SIGHASH_GROUP'' and ''SIGHASH_ANYPREVOUT''.</ref>
    


    vostrnad commented at 9:53 pm on August 3, 2023:

    A few suggestions:

    • Break italics for commas.
    • Improved wording on the difference between SIGHASH_ALL and SIGHASH_DEFAULT.
    • The footnote doesn’t say why DEFAULT/ALL is the preferred sighash flag, only why ANYONECANPAY is unsafe, so the footnote name should reflect that.
    • Remove italics from the link to section. This is not consistent throughout the document and other BIPs I checked don’t do this.
    0* The sender should sign with one of the sighash flags ''DEFAULT'', ''ALL'', ''SINGLE'', ''NONE'' (''ANYONECANPAY'' is unsafe). It is strongly recommended implementations use ''SIGHASH_ALL'' (''SIGHASH_DEFAULT'' for taproot inputs) when possible<ref name="why_not_sighash_anyonecanpay">'''Why is it unsafe to use ''SIGHASH_ANYONECANPAY''?''' Since the output address for the receiver is derived from from the sum of the [[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]] public keys, the inputs must not change once the sender has signed the transaction. If the inputs are allowed to change after the fact, the receiver will not be able to calculate the shared secret needed to find and spend the output. It is currently an open question on how a future version of silent payments could be made to work with new sighash flags such as ''SIGHASH_GROUP'' and ''SIGHASH_ANYPREVOUT''.</ref>
    
  116. in bip-0352.mediawiki:229 in 30f957d7e9 outdated
    224+=== Outpoints hash ===
    225+
    226+The sender and receiver MUST calculate an outpoints hash for the transaction in the following manner:
    227+
    228+* Collect each ''outpoint'' used as an input to the transaction
    229+* Sort the outpoints with ''sort<sub>outpoints</sub>(outpoints)''<ref name="why_sort_outpoints">'''Why are outpoints sorted before hashing?''' This way the silent payment otuput does not need to be recalculated if the wallet changes the order of inputs, e.g. at signing time or during an RBF bump.</ref>
    


    vostrnad commented at 9:53 pm on August 3, 2023:
    0* Sort the outpoints with ''sort<sub>outpoints</sub>(outpoints)''<ref name="why_sort_outpoints">'''Why are outpoints sorted before hashing?''' This way the silent payment output does not need to be recalculated if the wallet changes the order of inputs, e.g. at signing time or during an RBF bump.</ref>
    
  117. in bip-0352.mediawiki:242 in 30f957d7e9 outdated
    237+* ''P2TR''
    238+* ''P2WPKH''
    239+* ''P2SH-P2WPKH''
    240+* ''P2PKH''
    241+
    242+Inputs with conditional branches or multiple public keys (e.g. ''CHECKMULTISIG'') are not included 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 as described below.
    


    vostrnad commented at 9:53 pm on August 3, 2023:

    Might as well write out explicitly how we work around this since it’s not that much longer:

    0Inputs with conditional branches or multiple public keys (e.g. ''CHECKMULTISIG'') are not included 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.
    
  118. in bip-0352.mediawiki:295 in 30f957d7e9 outdated
    290+** For each ''B<sub>m</sub>'' in the group:
    291+*** Let ''t<sub>n</sub> = sha256(ser<sub>P</sub>(ecdh_shared_secret) || ser<sub>32</sub>(n))''
    292+*** Let ''P<sub>mn</sub> = B<sub>m</sub> + t<sub>n</sub>·G''
    293+*** Encode ''P<sub>mn</sub>'' as a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot output
    294+*** Optionally, repeat with n++ to create additional outputs for the current ''B<sub>m</sub>''
    295+*** If no additional outputs are required, continue to the next ''B<sub>m</sub>'' with ''n++''<ref name="why_not_the_same_tn">''' Why not re-use ''t<sub>n</sub>''? when paying different labels to the same receiver?''' If paying the same entity but to two separate labeled addresses in the same transaction without incrementing ''n'', the two outputs would be ''B<sub>spend</sub> + t<sub>n</sub>·G + i·G'' and ''B<sub>spend</sub> + t<sub>n</sub>·G + j·G''. The attacker could subtract the two values and observe that the distance between i and j is small. This would allow them to deduce that this transaction is a silent payment transaction and that a single entity received two outputs, but won't tell them who the entity is.</ref>
    


    vostrnad commented at 9:54 pm on August 3, 2023:

    Extra question mark:

    0*** If no additional outputs are required, continue to the next ''B<sub>m</sub>'' with ''n++''<ref name="why_not_the_same_tn">''' Why not re-use ''t<sub>n</sub>'' when paying different labels to the same receiver?''' If paying the same entity but to two separate labeled addresses in the same transaction without incrementing ''n'', the two outputs would be ''B<sub>spend</sub> + t<sub>n</sub>·G + i·G'' and ''B<sub>spend</sub> + t<sub>n</sub>·G + j·G''. The attacker could subtract the two values and observe that the distance between i and j is small. This would allow them to deduce that this transaction is a silent payment transaction and that a single entity received two outputs, but won't tell them who the entity is.</ref>
    
  119. in bip-0352.mediawiki:328 in 30f957d7e9 outdated
    323+
    324+* Generate the ''outpoints_hash'', using the method described above
    325+* Let ''A = A<sub>0</sub> + A<sub>1</sub> + … A<sub>n</sub>'', where each ''A<sub>i</sub>'' is the public key of an input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    326+* Let ''ecdh_shared_secret = outpoints_hash·b<sub>scan</sub>·A''
    327+* Check for outputs:
    328+** Let ''outputs_to_check = the taproot output key from each unspent taproot output in the transaction''
    


    vostrnad commented at 9:54 pm on August 3, 2023:
    0** Let ''outputs_to_check'' be the taproot output keys from all unspent taproot outputs in the transaction
    
  120. in bip-0352.mediawiki:359 in 30f957d7e9 outdated
    339+***** If a match is found:
    340+****** Add the ''P<sub>n</sub> + m·G'' to the wallet
    341+****** Remove ''output'' from ''outputs_to_check'' and rescan ''outputs_to_check'' with ''n++''
    342+***** If the label is not found, negate ''output'' and check again
    343+*** If no matches are found, stop
    344+
    


    vostrnad commented at 9:54 pm on August 3, 2023:
    There is no section for speding outputs, like there is for creating and scanning. While it’s not exceedingly difficult to figure out how to calculate the private key of a silent payment output once it’s detected in scanning, I think the procedure should be explicitly stated.
  121. vostrnad commented at 9:55 pm on August 3, 2023: contributor

    Doing another pass of review. This time it’s mostly nits which I take to be a good sign.

    Some general style nits:

    • Mixed usage of and ....
    • Declarations that use with a repeated operation should have the operation symbol appear on both sides of the ellipsis, but one of them is sometimes missing. (e.g. a1 … + an should be changed to a1 + … + an)
  122. josibake force-pushed on Aug 4, 2023
  123. petertodd commented at 5:51 pm on August 4, 2023: contributor

    NACK without fixing the lack of an expiration time.

    Rational: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-August/021849.html

    IMO better to discuss this issue on the bitcoin-dev mailing list, as it’s relevant to all potential new address formats.

  124. in bip-0352.mediawiki:194 in 478a5c54ea outdated
    189+
    190+A transaction is a ''Silent Payments v0'' transaction and MUST be scanned if and only if all of the following are true:
    191+
    192+* The transaction contains at least one BIP341 taproot output
    193+* The transaction has at least one input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    194+* The transaction does not spend a new, undefined output type (e.g. SegWit versions > 1)<ref name="skip_txs_with_unknown_prevouts">'''Why skip transactions that spend unknown output scripts?''' Skipping transactions that spend unknown output scripts allows us to have a clean upgrade path for Silent Payments by avoiding the need to scan the same transaction multiple times with different rule sets. If a fancy new output type is added in the future and Silent Payments v1 is released with support, we would want to avoid having to first scan the transaction with the silent payment v0 rules and then again with the silent payment v1 rules. Note: this restriction only applies to the inputs of a transaction.</ref>
    


    RubenSomsen commented at 10:02 pm on August 6, 2023:
    Should e.g. SegWit versions > 1 be i.e. SegWit versions > 1? Are there others that matter?

    vostrnad commented at 10:49 pm on August 6, 2023:
    CTV was originally proposed as a bare script output (<32 bytes> OP_CTV). I believe there was some effort to incorporate it into Tapscript, but the current version of the BIP seems to imply that CTV can be used anywhere, including bare scripts.

    vostrnad commented at 11:02 pm on August 6, 2023:
    More importantly, we never specify what the “defined output types” are. I automatically read this as all policy-standard output types, but standardness rules can change. What about larger than x-of-3 bare multisig that are non-standard but consensus-valid? How about bare OP_1 (emphemeral anchor)? This shouldn’t be left as an exercise for the reader.

    josibake commented at 9:09 am on August 9, 2023:

    Great point, I think what we need here is a “Forward compatibility” section. As of today, we have the “Inputs for shared secret derivation” and then anything else can be spent in the transaction, except for Segwit version > 1. We skip these because they might eventually be added to the “Inputs for shared secret derivation” list for a future silent payments version. For everything else, it’s fine to spend it in a transaction.

    Because of this, we need to specify that if a future soft fork uses an OP_NOP (bare script) for a soft fork, this new output type cannot be added to a future version’s “Inputs for shared secret derivation” list, unless using the v31 non-backward compatible upgrade. Additionally, future versions can only add to the “Inputs for shared secret derivation” list unless using the v31 non-backward compatible upgrade.


    josibake commented at 12:40 pm on November 1, 2023:
    @RubenSomsen same here, might be nice to flesh this out a bit more. IIRC, you also had some concerns with how forward compatibility is currently phrased in the BIP?
  125. in bip-0352.mediawiki:182 in 478a5c54ea outdated
    177+
    178+''v31'' (l) is reserved for a backwards incompatible change, if needed. For ''Silent Payments v0'':
    179+
    180+* If the receiver's silent payment address version is:
    181+** ''v0'': check that the data part is exactly 66-bytes. Otherwise, fail
    182+** ''v1'' through ''v30'': read the first 66-bytes of the data part and discard the remaining bytes (if any)
    


    RubenSomsen commented at 10:06 pm on August 6, 2023:
    Should we be explicit about an upper bound here? Otherwise some v0 wallets may not be able to read v1+ if it’s longer than they can read. Elsewhere regarding bech32m we say it is recommended implementations use a limit of 1023 characters.

    josibake commented at 11:32 am on November 1, 2023:
    updated!
  126. in bip-0352.mediawiki:355 in 478a5c54ea outdated
    344+
    345+==== Backup and Recovery ====
    346+
    347+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.
    348+
    349+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 or generates its change addresses using the change label, this information SHOULD be included in the backup. If the user does not know whether labels or the change label were used, it is strongly recommended they always check for the change label when recovering from backup and precompute 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.
    


    RubenSomsen commented at 10:14 pm on August 6, 2023:

    If a wallet uses labels or generates its change addresses using the change label, this information SHOULD be included in the backup.

    I think we should just always scan for change. I don’t see much of a downside and it simplifies things. What if a user switches from a non-change wallet to a change wallet. Do they now have to redo the backup?

    and precompute a large number of labels (e.g. 100k labels)

    I’d also argue here for a stronger statement on a default.


    josibake commented at 12:40 pm on November 1, 2023:
    this should be addressed now with your recent edits
  127. in bip-0352.mediawiki:382 in 478a5c54ea outdated
    371+            "outpoints": [<array of tuples, where each tuple represents an outpoint: (txid, vout)>],
    372+            "input_priv_keys": [<array of tuples, where each tuple is a hex encoded private key and boolean for taproot: (priv_key, is_taproot)>],
    373+            "recipients": [<array of tuples, where each tuple is a bech32m string representing a silent payment address and an float amount: (silent_payment_address, amount)>]
    374+        },
    375+        "expected": {
    376+            "outputs": [<array of key, value objects, where the key is the hex encoding of 32-byte x-only public key and the value an integer amount: {taproot_x_only_key: amount}]
    


    cygnet3 commented at 10:16 pm on August 7, 2023:

    Test vector format has since updated:

    0            "outputs": [<array of tuples, where each tuple is the hex encoding of 32-byte x-only public key and an integer amount: (taproot_x_only_key, amount)]
    
  128. cygnet3 commented at 5:03 pm on August 8, 2023: none

    Regarding the test vectors: I think it is best to remove the ‘amount’ values from the sending tests.

    Payment amounts don’t test any logic about silent payments directly. Rather, they are indirectly used for the following two purposes:

    1. to match generated taproot outputs with a specific recipient (because all the amounts are unique)
    2. to ensure determinism in testing (sorting on amounts for every silent payment group)

    In other words, they are used as a sort-able unique identifier for each recipient.

    Both of these goals can be achieved in another way:

    1. For the sending outputs, expect a dictionary object instead, where the key is a recipient (bech32m string representing a silent payment address), and the value is a list of taproot outputs (hex encoding of 32-byte x-only public key) generated for this recipient.
    2. In the tests, sort by Bm where we currently sort by amount. This shouldn’t reveal any extra information in the generated outputs, since these outputs are calculated as Pmn = Bm + tn·G and tn is a sha256 digest.

    Ordering of a ECPubKey does not seem to be implemented in the simplified secp256k1.py test library, but it is defined in the secp256k1 library itself. I have access to it in the rust-secp256k1 library via the Ord and PartialOrd trait implementations of the PublicKey struct.

    I think doing (1) at least makes a lot of sense. SP implementations will always need to be able to map outputs with specific recipients, so the tests should reflect that.

    For (2) I am less certain. Regardless, if the only point of ‘amounts’ is to create deterministic test results, it would be better to rename amount to order or something.

  129. Sosthene00 commented at 5:50 pm on August 8, 2023: none

    Regarding the test vectors: I think it is best to remove the ‘amount’ values from the sending tests.

    Payment amounts don’t test any logic about silent payments directly. Rather, they are indirectly used for the following two purposes:

    1. to match generated taproot outputs with a specific recipient (because all the amounts are unique)
    
    2. to ensure determinism in testing (sorting on amounts for every silent payment group)
    

    In other words, they are used as a sort-able unique identifier for each recipient.

    Both of these goals can be achieved in another way:

    1. For the sending outputs, expect a dictionary object instead, where the key is a recipient (bech32m string representing a silent payment address), and the value is a list of taproot outputs  (hex encoding of 32-byte x-only public key) generated for this recipient.
    
    2. In the tests, sort by Bm where we currently sort by amount. This shouldn't reveal any extra information in the generated outputs, since these outputs are calculated as Pmn = Bm + tn·G and tn is a sha256 digest.
    

    Ordering of a ECPubKey does not seem to be implemented in the simplified secp256k1.py test library, but it is defined in the secp256k1 library itself. I have access to it in the rust-secp256k1 library via the Ord and PartialOrd trait implementations of the PublicKey struct.

    I think doing (1) at least makes a lot of sense. SP implementations will always need to be able to map outputs with specific recipients, so the tests should reflect that.

    For (2) I am less certain. Regardless, if the only point of ‘amounts’ is to create deterministic test results, it would be better to rename amount to order or something.

    I totally support this, and even for determinism frankly I already got rid of the amounts in my own test and it doesn’t seem to affect me.

    I would change some other details about the tests and test vectors, I’ll make a comment about that specifically soon.

  130. josibake commented at 9:28 am on August 9, 2023: member

    @Sosthene00 @cygnet3 Thanks for the suggestions! I think just sorting the labeled spend pub keys is likely the best solution. For sorting, we can just lexicographically sort the compressed format (which is what secp2561 does).

    Also fine with removing amounts, so long as we can be sure the correct amounts are being paid by the generated taproot outputs. @Sosthene00 I’ll wait for the rest of your feedback before updating the tests

  131. josibake force-pushed on Aug 9, 2023
  132. josibake force-pushed on Aug 9, 2023
  133. FeatureSpitter commented at 7:57 am on September 2, 2023: none

    What do you mean by “without requiring any interaction or on-chain overhead”?

    You still have to record a new transaction in the ledger with this new UTXO targeting the “silent tweaked address” (X' = hash(i*X)*G + X). Thus I don’t understand where don’t you have on-chain overhead or what you mean by it.

    Thanks

  134. in bip-0352.mediawiki:70 in c55f80c53c outdated
    65+
    66+* Let ''k = 0''
    67+* Let ''P<sub>0</sub> = B + hash(a·B || n)·G''
    68+* For additional outputs:
    69+** Increment ''k'' by one (''k++'')
    70+** Let ''P<sub>i</sub> = B + hash(a·B || n)·G''
    


    stevenroose commented at 4:03 pm on September 2, 2023:
    You seem to be mixing k and n here?

    RubenSomsen commented at 3:01 pm on September 12, 2023:
    Looks like you’re right, will fix it.

    josibake commented at 12:43 pm on November 1, 2023:
    This should be fixed now
  135. in bip-0352.mediawiki:87 in c55f80c53c outdated
    82+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:
    83+
    84+* Let ''outpoint_hash = hash(txid || vout)''
    85+* Let ''P<sub>0</sub> = B + hash(outpoint_hash·a·B || 0)·G''
    86+
    87+Bob must include the same ''outpoint_hash'' when scanning.
    


    stevenroose commented at 4:04 pm on September 2, 2023:
    Why multiply and not concatenate?

    RubenSomsen commented at 12:50 pm on September 12, 2023:
    The multiplication saves bandwidth for the light client use case. Full nodes can serve a single 33 byte value outpoints_hash*A as opposed to having to serve outpoints_hash and A separately (65 bytes). The client can then multiply this value with b and calculate P.

    stevenroose commented at 12:23 pm on September 13, 2023:
    Neat, that makes sense.

    real-or-random commented at 3:14 pm on October 11, 2023:

    Hm, I had the same question. Your answer makes sense, but I worry that there can be strange interactions of the multiplicands.

    For example, with two different outpoint_hash != outpoint_hash', I can create

    • P0 = B + hash(outpoint_hash · a · B || 0)·G and
    • P0' = B + hash(outpoint_hash' · a' · B || 0)·G = P0 where a' = a · outpoint_hash / output_hash'

    Is this a problem? I don’t know. At least, that seems to contradict the idea that even malicious senders shouldn’t be able to create collisions (without creating double spends). And the receiver can do the same thing.

    If you want to avoid this but still have 33 bytes, you could do two steps of tweaking:

    • B' = B + hash(outpoint_hash||B) · G
    • P0 = B' + hash(a' · B' || 0) · G

    (Hm, I have a suspicion that now you will tell me that this breaks something because now there’s a B in the first hash…)


    josibake commented at 12:55 pm on November 1, 2023:

    Is this a problem? I don’t know

    I think this falls into the category of “you might be able to force address re-use while following the protocol, but why not just send directly to the last address you created for the receiver.” But it is a good point, in that perhaps the “malice” here is not to force address reuse for the receiver, but to erode confidence in the protocol by forcing address reuse while still following the protocol (i.e. “silent payments is broken”).

    Regarding your proposal, how does this work with B_scan and B_spend? My intuition is that it breaks the label scanning for the receiver, but I’m not sure..


    RubenSomsen commented at 3:46 pm on November 16, 2023:

    @real-or-random

    Apologies for the late reply, @josibake informed me that this was still unaddressed.

    Initially I wanted to say a' = a · outpoint_hash / outpoint_hash' has a circular reference, because outpoint_hash' contains the hash of a' and thus you can never actually calculate a'.

    However, we’re currently discussing the possibility of hashing only one outpoint instead of all of them, and then this protective circular reference would be gone.

    I think your suggested mitigation still requires 65 bytes for light clients. Light clients can’t calculate B' = B + hash(outpoint_hash||B) · G followed by P0 = B' + hash(b' · A' || 0) · G without learning both outpoint_hash (32 bytes) and A' (33 bytes).

    It seems to me that another way to fix this is by reintroducing the circular reference with P0 = B + hash(hash(outpoint_hash || A)·b·A || 0)·G (also doesn’t require another ECC multiplication) . Now light clients only need to learn hash(outpoint_hash || A)·A (33 bytes) to calculate P0.


    josibake commented at 7:02 am on December 6, 2023:

    In summary, I think only hashing one outpoint is the right way to go, so this is something we should address.

    You are correct @RubenSomsen that @real-or-random ’s proposal requires 65 bytes for light clients, and I don’t see any issues with your proposal of hash(outpoint || A)·A. The only correction would be we can concatenate the outpoint and A directly (no need to hash the outpoint first). I’m assuming this is what you meant, but just wanted to clarify. To check my understanding, this works because A is the sum of silent payment eligible inputs, so any time the attacker tries to maliciously choose a', A' would be included in the hash. @real-or-random thoughts on the updated proposal?


    josibake commented at 4:29 pm on December 11, 2023:
    also, @benma can you confirm this is feasible from a HWW perspective?

    real-or-random commented at 12:11 pm on January 5, 2024:
    @RubenSomsen @josibake I agree with your comments, and the changes in 0985f299b3420680cdc32f56f01391e3c5b8064f look good to me.
  136. Sosthene00 commented at 10:51 am on September 4, 2023: none

    What do you mean by “without requiring any interaction or on-chain overhead”?

    You still have to record a new transaction in the ledger with this new UTXO targeting the “silent tweaked address” (X' = hash(i*X)*G + X). Thus I don’t understand where don’t you have on-chain overhead or what you mean by it.

    Thanks

    Hi, I think it just means that we don’t need extra transactions beside the payment and don’t add data to transactions either, like BIP47 or Private Payment that both need a notification transaction with some data in an OP_RETURN.

  137. stevenroose changes_requested
  138. stevenroose commented at 6:44 pm on September 7, 2023: contributor
    This BIP seems to be structured as “let’s gradually build a silent payment construction by improving in stages”. That’s more what a blogpost would be. For the spec, I would prefer to just have a description of the final construction instead of all the intermediate ones. Right now it’s kinda confusing, you might be skimming and read something in the middle that is actually not part of the actually proposed construction..
  139. RubenSomsen commented at 3:47 pm on September 12, 2023: contributor

    Thanks for the feedback @stevenroose

    I would prefer to just have a description of the final construction

    In the opening paragraph of the Overview we explicitly mention this:

    We first present an informal overview of the protocol. [...] 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.

    While I see how it can also lead to confusion in some cases, we’ve also received a lot of feedback on how this made it easier to comprehend the protocol. There are a lot of steps involved, and this gives us a good framework of introducing them to the reader one by one.

  140. in bip-0352.mediawiki:105 in 70f1e5f319 outdated
    100+
    101+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<sub>scan</sub>, B<sub>spend</sub>)''. This allows Bob to keep ''b<sub>spend</sub>'' in offline cold storage and perform the scanning with the public key ''B<sub>spend</sub>'' and private key ''b<sub>scan</sub>''. Alice performs the tweak using both of Bob's public keys in the following manner:
    102+
    103+* Let ''P<sub>0</sub> = B<sub>spend</sub> + hash(outpoints_hash·a·B<sub>scan</sub> || 0)·G''
    104+
    105+Bob detects this payment by calculating ''P<sub>0</sub> = B<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A)·G'' with his online device and can spend from his cold storage signing device using ''(b<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A)) mod p'' as the private key.
    


    vostrnad commented at 3:26 pm on September 17, 2023:
    0Bob detects this payment by calculating ''P<sub>0</sub> = B<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A)·G'' with his online device and can spend from his cold storage signing device using ''(b<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A)) mod n'' as the private key.
    

    josibake commented at 12:59 pm on November 1, 2023:
    Fixed!
  141. in bip-0352.mediawiki:241 in 70f1e5f319 outdated
    236+* ''P2TR''
    237+* ''P2WPKH''
    238+* ''P2SH-P2WPKH''
    239+* ''P2PKH''
    240+
    241+Inputs with conditional branches or multiple public keys (e.g. ''CHECKMULTISIG'') are not included 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. Note that with the exception of P2TR, all of the above supports [https://bitcoin.stackexchange.com/questions/57855/c-secp256k1-what-do-prefixes-0x06-and-0x07-in-an-uncompressed-public-key-signif hybrid pubkeys].
    


    vostrnad commented at 3:26 pm on September 17, 2023:
    Hybrid pubkeys are only supported in P2PKH, as SegWit requires compressed keys by consensus. The sentence should probably be rewritten and placed under the P2PKH section, e.g. “Note that hybrid pubkeys are supported in P2PKH.”

    josibake commented at 1:03 pm on November 1, 2023:
    I think we should probably just disallow hybrid keys all together and be more explicit about the script template matching

    SomberNight commented at 3:17 pm on November 9, 2023:

    SegWit requires compressed keys by consensus

    No, that is only enforced at policy level: https://github.com/bitcoin/bitcoin/blob/b3898e946cf81e2e7b573e1c5204bd29af2feecd/src/policy/policy.h#L112


    vostrnad commented at 3:21 pm on November 9, 2023:
    @SomberNight You’re right, I assumed since this is included in BIP141 that it’s a consensus rule but I missed that it’s only a policy rule.

    josibake commented at 9:57 pm on January 10, 2024:
    I’ve updated this section to only use X-only and compressed keys. This makes things much simpler and avoids going through each output type and specifying which supports uncompressed/hybrid and under which circumstances (standard, non-standard).
  142. in bip-0352.mediawiki:425 in 70f1e5f319 outdated
    420+
    421+==== Receiving ====
    422+
    423+* Ensure the public key can be extracted from non-standard ''P2PKH'' scriptSigs
    424+* Ensure taproot script path spends are included, using the taproot output key (unless ''H'' is used as the taproot internal key)
    425+* Ensure the scanner can extract the public key from each of the input types supported (e.g. ''P2WPKH'', ''P2SH-P2WPKH'', etc)
    


    vostrnad commented at 3:27 pm on September 17, 2023:
    0* Ensure the scanner can extract the public key from each of the input types supported (e.g. ''P2WPKH'', ''P2SH-P2WPKH'', etc.)
    
  143. in bip-0352.mediawiki:230 in 70f1e5f319 outdated
    225+
    226+The sender and receiver MUST calculate an outpoints hash for the transaction in the following manner:
    227+
    228+* Collect each ''outpoint'' used as an input to the transaction
    229+* Let ''outpoints = outpoint<sub>0</sub> || ... || outpoint<sub>n</sub>'', sorted lexicographically by txid and vout, ascending order<ref name="why_sort_outpoints">'''Why are outpoints sorted before hashing?''' This way the silent payment output does not need to be recalculated if the wallet changes the order of inputs, e.g. at signing time or during an RBF bump. Note that the silent payment output does need to be recalculated if inputs are added or removed.</ref>
    230+* Let ''outpoints_hash = sha256(outpoints)''
    


    benma commented at 8:38 pm on September 26, 2023:

    This is problematic for hardware wallets. Hardware wallets are constrained in memory and may not be able to load all outpoints and sort them. As a result, they would have to restrict transactions to already have sorted inputs at signing time, which is not desirable and would lead to compatibility issues.

    Maybe instead of using the hash of the sorted outpoints, we could use something like:

    sha256(outpoint_0) + sha256(outpoint_1) + ... + sha256(outpoint_n), which does not depend on the ordering of the outpoints.

    It would be great if a cryptographer could validate if the above or something similar works and if it is safe.


    josibake commented at 5:35 pm on October 2, 2023:
    Thanks for the suggestion @benma ! I’m leaning toward your suggestion (assuming there are no issues cryptographically). Not needing a sort feels less brittle than needing to worry about having the outpoints in the right order before hashing. The only open question I have is how much would this affect the scanning time for a full-node. I’ll make a benchmark and get back to you on that.

    sipa commented at 2:45 am on October 3, 2023:
    Without looking much into the context, but “sum modulo big number of hashes of elements” is very much not a secure way of computing a hash of a set. Whether that’s a problem depends on course on what properties you need of that hash function, but it will in general not be preimage resistant.

    jonasnick commented at 8:02 am on October 3, 2023:

    it will in general not be preimage resistant

    See Wagner’s algorithm for the generalized birthday problem.


    josibake commented at 8:44 am on October 3, 2023:

    Thanks @sipa , @jonasnick ! For context, we hash the outpoints used in the transaction to come up with a unique scalar to multiply the ECDH shared secret with. The shared secret is then hashed again before creating the final output. The purpose of this scalar is to prevent generating the same scriptPubKey for the receiver when spending UTXOs from the same sender scriptPubKeys (e.g. a large exchange spending from a scriptPubKey that has been used multiple times).

    What we need is for this scalar to be different for each set of UTXOs that come from a reused scriptPubKey, being sent to the same receiver. If I’m understanding your points correctly, the concern here with hashing each outpoint and summing is that we might end up with the same scalar for two different sets of UTXOs?


    benma commented at 8:47 am on October 3, 2023:

    Thanks @sipa and @jonasnick.

    Correct me if I am wrong @josibake @RubenSomsen, but the point of the hash of the inputs was not preimage resistance, but just to make sure the output pubkey changes when the inputs change to avoid pubkey reuse.

    In that case, summing instead of hashing should still work.

    The preimage of the outpoints hash is public anyway, as it is part of the transaction.


    josibake commented at 12:27 pm on October 3, 2023:
    that’s correct @benma regarding the purpose and the fact that the preimage is known. I think the more important question is whether or not this approach is collision-resistant: if hashing and summing a large number of outpoints, what is this risk we end up with the same hash as a different set of outpoints when spending from the same scriptPubKey?

    sipa commented at 12:39 pm on October 3, 2023:

    Are we talking about an adversarial setting or not? If you’re only concerned about honestly-constructed sets of outpoints, this is fine in the sense that the result is uniformly distributed. If you are talking about an adversarial setting, probability isn’t what you should be concerned about, only cost to make it deliberately fail.

    Deliberately constructing a set of say 32 (valid) outpoints such that the sum of their hashes matches a fixed value is completely feasible (I’d guess a few hours of CPU time). Collisions are even easier.

    Secure set hashes do exist, but they’re a bit more involved. The MuHash construction used in Bitcoin Core’s utxo set hash is one of them. All set elements are hashed to a 3072-bit value there, and those values are multiplied modulo a big prime. Then the result of that is hashed down to 256 bits.


    benma commented at 1:38 pm on October 3, 2023:

    Thanks - good to know that in the honest setting, it wold work.

    Deliberately constructing a set of say 32 (valid) outpoints such that the sum of their hashes matches a fixed value is completely feasible (I’d guess a few hours of CPU time). Collisions are even easier. @sipa for clarity: the sender would generate the UTXOs first by locally brute forcing transactions creating such outputs until they result in some fixed value, right?

    About the adversarial setting: failure in this case means the outpoints are chosen so that the output key has a collision (output script or address reuse). Since the ECDH shared secret is involved, only the sender can invoke this failure.

    • The sender can do this regardless of the hashing, as they can just send to the same output script using any kind of transaction. The recipient might not find the payment unless they also scan for other transactions paying to a silent payment output. @josibake is a recipient supposed to do that?
    • Does a sender have any motivation to perform this kind of work just to be able to reuse an address (output script) of the recipient? The recipient can detect that this happened easily enough.

    Anything I missed about the adversarial case?


    josibake commented at 2:09 pm on October 3, 2023:

    Regarding the adversarial case, perhaps this is an issue in a collaborative spend (coinjoin): the non-sending participants could craft malicious outpoints such that they force the sender to re-create a scriptPubKey from a previous send. But I don’t think this is feasible given that they would need to know ahead of time the recipient address (which we are hiding with blind DH) and which UTXOs the honest participant (sender) intends to spend.

    Also, as @benma mentioned, we can’t stop malicious address reuse since the sender can send to a previously created scriptPubKey at any time. The outpoint hash is only to prevent honest participants from accidentally recreating a scriptPubkey when sending to the same receiver and spending from a scriptPubKey that has been reused.

    The recipient might not find the payment unless they also scan for other transactions paying to a silent payment output. @josibake is a recipient supposed to do that?

    This is more of an implementation detail for wallets: does your wallet keep checking for scriptPubKeys it has already learned about. I imagine most wallets already do this, and I would certainly recommend a silent payments wallet also check previously found SP outputs when scanning a new tx/block.


    josibake commented at 10:37 am on October 10, 2023:

    After thinking this over some more, our best option is to remove the sorting step. This means that the sender and receiver hash the outpoints in the order they appear in the final transaction. This was our original proposal and the sorting step was added to ensure the silent payments output was not dependent on the transaction structure, as a “belt and suspenders” approach.

    But the order of the inputs is committed to when signing the transaction, and in the hardware wallet context, it is during signing that the silent payment output would be created anyway, so I think this is fine. For software wallets, we just need to make it explicit in the BIP that you must not change the order of the inputs after generating the silent payments output, e.g. generate the silent payment output when signing the transaction.

    The benefits of removing the sort are:

    1. Easier for hardware wallets to implement
    2. Small improvement in scanning time

    Curious to here your thoughts @RubenSomsen , @benma ?


    vostrnad commented at 5:17 pm on October 10, 2023:
    Doesn’t the hardware wallet need to load the entire transaction in order to sign it anyway? Is the small overhead for sorting the outpoints really a problem?

    benma commented at 5:28 pm on October 10, 2023:
    @josibake no additional and sorting and using the inputs as they are ordered in the transaction works for hardware wallets and would also be the simplest to implement. Out of curiosity, why did you reject the alternative of summing the hashes? @vostrnad no, hardware wallets can’t generally load the entire transaction due to limited memory - they can stream it though and process e.g. one input at a time. Many wallets operate like this today, e.g. BitBox02, Ledger and Trezor.

    vostrnad commented at 5:31 pm on October 10, 2023:
    @benma I see, then wouldn’t it be possible for the connected software to stream the sorted outpoints as well? Verifying that they’re sorted correctly would only require a constant amount of memory.

    benma commented at 6:05 pm on October 10, 2023:

    @vostrnad it would be possible but it would harm UX and would add significant technical complexity.

    UX: the inputs would need to be streamed twice instead of once, which takes roughly twice as long.

    Technical complexity: the hardware wallet would need to verify that the inputs streamed in the second round are the same as in the first round, which means e.g. maintaining a merkle tree in both the host software and on the device. Adding a merkle tree implementation adds binary bloat and some HW wallets might have to implement a merkle tree with an insert() and verify() function from scratch.

    So it is doable but ideally avoided.


    real-or-random commented at 2:43 pm on October 11, 2023:

    I agree that a simple and clean solution is just to drop the sorting. If without a concrete concern w.r.t. hw wallets, I feel that the sorting adds too much complexity given that it’s not clear what it actually achieves.

    Here are some more thoughts on this: I agree with sipa and nickler that summing hashes makes them prone to collisions due to Wagner’s attack (similar attacks such as BLLOR). But collision resistance is a goal here because this is what will “prevent generating the same scriptPubKey for the receiver when spending UTXOs from the same sender scriptPubKeys”. Or at least, this ensures that when there are transactions with colliding scriptPubKeys for the receiver, only one of them will be valid because they all spend the same UTXOs.

    This hints at another possible solution: Hash only the first outpoint. This ensures that any transactions with colliding scriptPubKeys for the receiver will be double-spends. A transaction doesn’t need to double-spend all the inputs to be invalid, one input suffices… ^^ This is even more efficient when it comes to hashing, perhaps at the cost that it looks bit inelegant.


    vostrnad commented at 3:08 pm on October 11, 2023:
    If we’re hashing only one outpoint (I don’t see any immediate problem with that, thanks for the suggestion @real-or-random), we might as well use the lowest (lexicographically) outpoint and retain the benefits of sorting (which are outlined in the footnote).

    Sosthene00 commented at 11:11 am on October 13, 2023:

    Hi, I’m catching up on the discussion here, I just would like to sum up what I understood of the issue because it’s not entirely clear for me.

    If I understand correctly the problem with the way we are using a hash of all outpoints here, is that it is theoretically relatively easy to find 2 sets of different outpoints that will produce the same hash. If we suppose that we also have the same set of keys in the scriptpubkeys those outpoints point to, a malicious payer would be able to make 2 different transactions (no double spend) that pays the same silent payment destination keys, forcing address reuse to the receiver, correct?

    I would just confirm that I got this right before I elaborate further. (Anyway I like the last proposal from @real-or-random it seems to me that it fulfills the purpose of the outpoints hash in a simpler way, but I need to think more about it)


    josibake commented at 12:39 pm on October 16, 2023:

    Out of curiosity, why did you reject the alternative of summing the hashes?

    I ran a rudimentary benchmark and the hashing and summing technique was quite a bit slower compared to hashing a concatenation of the bytes. Over the course of the conversation, I’ve also come to agree that hashing and summing or sorting then hashing seem unjustifiably complex, given that it is only to guard against a theoretical implementation bug.

    we might as well use the lowest (lexicographically) outpoint and retain the benefits of sorting (which are outlined in the footnote).

    This strikes a nice balance: simple while still retaining the benefits of sorting. @benma curious to hear your thoughts on this. Seems you could stream the lexicographically lowest input first, then stream all the inputs (so you would be streaming N+1 inputs), or could keep track of which input is the lowest until all the inputs have been streamed before proceeding with the silent payments protocol?


    josibake commented at 12:46 pm on October 16, 2023:

    @Sosthene00

    If I understand correctly the problem with the way we are using a hash of all outpoints here

    If by “here” you mean the newly proposed hash each outpoint and then sum the hashes, then yes: the concern is that it is easier to find collisions and also requires the scanner to do more work. Regarding collisions, this is only an issue in terms of honest collisions, as a malicious actor can just force address reuse by sending to a previously generated scriptPubKey.

    The issue with the old solution (sort the outpoints, concatenate, then hash) is that it is generally complex and specifically difficult for memory-constrained devices such as hardware wallets.

    Taking all of that into account, I think hashing the lexicographically lowest outpoint is likely the best solution.


    benma commented at 1:13 pm on October 16, 2023:
    @josibake using the hash of the lexicographically smallest input would work for hardware wallets, I don’t see any problems with this approach.

    Sosthene00 commented at 8:30 pm on October 16, 2023:

    If by “here” you mean the newly proposed hash each outpoint and then sum the hashes, then yes: the concern is that it is easier to find collisions and also requires the scanner to do more work. Regarding collisions, this is only an issue in terms of honest collisions, as a malicious actor can just force address reuse by sending to a previously generated scriptPubKey.

    Sorry I wasn’t very clear about the scheme I was talking about, thanks for correcting me. It seemed to me that it was quite a reach for a malicious actor indeed, as it would be quite hard (impossible?) to pull in practice even for say coinjoins, and would achieve little more than simply reusing an existing address from a previous transaction, but I wanted to make sure I didn’t miss something.

    Taking all of that into account, I think hashing the lexicographically lowest outpoint is likely the best solution.

    But if we all agree this is better, my question becomes irrelevant anyway.


    josibake commented at 1:02 pm on November 1, 2023:
    @RubenSomsen looks like rough consensus on using hashing the lowest outpoint (lexicographic). AFAICT, this doesn’t cause any issues for collaborative use cases as we were requiring all of the outpoints before, and now we are just picking one from all of the outpoints.
  144. in bip-0352.mediawiki:116 in 70f1e5f319 outdated
    104+
    105+Bob detects this payment by calculating ''P<sub>0</sub> = B<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A)·G'' with his online device and can spend from his cold storage signing device using ''(b<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A)) mod p'' as the private key.
    106+
    107+''' Labels '''
    108+
    109+For a single silent payment address of the form ''(B<sub>scan</sub>, B<sub>spend</sub>)'', 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<sub>spend</sub>'' with an integer ''m'' in the following way:
    


    fjahr commented at 7:18 pm on October 25, 2023:
    I find this paragraph a bit confusing. The label tweaking scheme described below also means Bob publishes multiple silent payment addresses. He is just keeping the scanning key unchanged.

    fjahr commented at 9:17 am on October 26, 2023:

    In this section I think it would be also good to note that the motivation to implement labels basically as the simplest possible derivation scheme is easier scanning, right?

    Aside from the k++ note it may be also interesting to mention that given two different SP addresses with the same spend key the users could calculate the “label diff”. That may give some hints on the usage of labels that is interesting for attackers.

    It is also possible to spam labels if incremented integers are used as recemmended, right? E.g. given B0 I can calculate B1, B2 etc. myself by just adding G and send funds there to see what happens with them. This makes the label unreliable as the identifier of the source of the incoming payment.


    RubenSomsen commented at 1:50 pm on November 2, 2023:

    Point taken on the confusion around the term “silent payment address”.

    And I believe we’ve addressed the concerns around labels in the latest version of the BIP by making each label a unique 32 byte value.


    fjahr commented at 2:24 pm on November 3, 2023:
    Hm, I don’t think I see that change or maybe I am misunderstanding it? Currently, the BIP version here still talks about m as an “incremental integer starting from 1”.

    RubenSomsen commented at 1:22 pm on November 9, 2023:
    The integer is still there but what has changed is that it’s now hashed together with a secret. The label is no longer equivalent to m but it’s now hash(bscan || m) where bscan and m are never revealed to outside observers. This makes it deterministic to recipients while indistinguishable from random to senders.

    josibake commented at 12:40 pm on December 6, 2023:
    Resolving this, let me know if you still have questions about this @fjahr
  145. in bip-0352.mediawiki:177 in 70f1e5f319 outdated
    173+|-
    174+!+24
    175+|c||e||6||m||u||a||7|| -
    176+|}
    177+
    178+''v31'' (l) is reserved for a backwards incompatible change, if needed. For silent payments v0:
    


    fjahr commented at 7:34 pm on October 25, 2023:
    I am not sure where would go after this. Let’s say we discover some flaw quickly after the first deployment or we have a cool new idea and then need to make a breaking change and jump to v31. Then what version do we use when we get segwit v2 or something similar? Doesn’t it seem more reasonable to reserve anything v24+ or so?

    RubenSomsen commented at 1:48 pm on November 2, 2023:
    We did consider that. The idea we landed on is to simply introduce a new version byte if we ever needed v31.
  146. in bip-0352.mediawiki:32 in 184a48a77f outdated
    27+
    28+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<ref name="out_of_band_notifications">'''Why not use out-of-band notifications''' Out of band notifications (e.g. using something other than the Bitcoin blockchain) have been proposed as a way of addressing the privacy and cost concerns of using the Bitcoin blockchain as a messaging layer. This, however, simply moves the privacy and cost concerns somewhere else and increases the risk of losing money due to a notification not being reliably delivered, or even censored, and makes this notification data critical for backup to recover funds.</ref>. 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.
    29+
    30+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|Appendix A: Light Client Support]]).
    31+
    32+The design keeps collaborative transactions such as CoinJoins and inputs with MuSig and FROST 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.
    


    vostrnad commented at 10:43 pm on November 2, 2023:
    0The 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.
    
  147. in bip-0352.mediawiki:53 in 184a48a77f outdated
    48+* Light client/SPV wallet support
    49+* Protocol is upgradeable
    50+
    51+== Overview ==
    52+
    53+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, ''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|Specification]].
    


    vostrnad commented at 10:43 pm on November 2, 2023:

    The notation is defined in “Specification” so I don’t think it’s necessary to repeat it here:

    0We first present an informal overview of the protocol. 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|Specification]].
    

    RubenSomsen commented at 1:17 pm on November 16, 2023:
    Fair point, but it doesn’t hurt and still seems useful so people can understand the segment without skipping ahead.
  148. in bip-0352.mediawiki:119 in 184a48a77f outdated
    114+* Publish ''(B<sub>scan</sub>, B<sub>0</sub>)'', ''(B<sub>scan</sub>, B<sub>1</sub>)'' etc.
    115+
    116+Alice performs the tweak as before using one of the published ''(B<sub>scan</sub>, B<sub>m</sub>)'' pairs. Bob detects the labeled payment in the following manner:
    117+
    118+* Let ''P<sub>0</sub> = B<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A || 0)·G''
    119+* Subtract ''P<sub>0</sub>'' from each of the transaction outputs and check if the remainder matches any of the labels (''hash(b<sub>scan</sub> || 1)·G'', hash(b<sub>scan</sub> || 2)·G'' etc.) that the wallet has previously used
    


    vostrnad commented at 10:44 pm on November 2, 2023:
    0* Subtract ''P<sub>0</sub>'' from each of the transaction outputs and check if the remainder matches any of the labels (''hash(b<sub>scan</sub> || 1)·G'', ''hash(b<sub>scan</sub> || 2)·G'' etc.) that the wallet has previously used
    
  149. in bip-0352.mediawiki:133 in 184a48a77f outdated
    128+
    129+== Specification ==
    130+
    131+We use the following functions and conventions:
    132+
    133+* The constant ''n'' refers to the secp256k1 curve order, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141''.
    


    vostrnad commented at 10:44 pm on November 2, 2023:
    n is defined in BIP340 so it doesn’t need to be here as well. Another option would be to put all definitions here and drop the reference to BIP340, but it is quite a lot of definitions.

    josibake commented at 12:43 pm on December 6, 2023:

    Good point, I think I’d prefer to reference BIP340, something like:

    0* The constant ''n'' refers to the secp256k1 curve order (defined in BIP340)
    

    vostrnad commented at 3:18 pm on December 6, 2023:
    BIP340 is already referenced in “For everything not defined above, we use the notation from BIP340”, so it doesn’t need to be referenced again here. On the other hand, this sounds like it’s saying the curve order is defined in BIP340, which it’s not (it’s not defined anywhere, it follows from the curve parameters).

    sipa commented at 6:08 pm on December 6, 2023:

    From BIP340:

    • The constant n refers to the curve order, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141.

    vostrnad commented at 6:20 pm on December 6, 2023:
    @sipa Yes, n is defined to be the curve order, however BIP340 doesn’t define what the curve order itself is, and the proposed edit kind of makes it sound like it does.

    real-or-random commented at 12:43 pm on January 5, 2024:

    What adds to the confusion here is that $n$ is currently also used as the number of pubkeys $A_i$. And $n$ refers to the curve order only at one location in the doc, namely in the checks “if $t_k=0$ or $t_k \ge n$, fail” (twice).

    I suggest getting rid of the bullet point “The constant $n$ refers to the curve order” and doing one of the following:

    • Simply remove the checks. " $t_k=0$ or $t_k \ge n$" occurs with negligible (=astronomically low) probability assuming the hash function is good, i.e., this won’t occur in practice, and even if were to occur here, this won’t be the end of the world if you think about it. [1] This boils down to
    • Rephrase to “if $t_k$ is not valid tweak, i.e., if $t_k = 0$ or $t_k$ is larger or equal to the secp256k1 group order”
    • Or slightly nicer, introduce a function valid_tweak(t) which definition uses the words “secp256k1 group order” or simply the integer literal “0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141”

    (I think you also want to add an int call when defining $t_k$… Currently, it’s a hash, so the type is a byte array, and then comparisons such as “$t_k \ge n$” don’t make a lot of sense.)

    [1] There tends to be a lot of bike shedding about these practically unreachable cases… Here’s a hopefully convincing argument for ignoring them: All other things equal, ignoring them removes error cases and thus makes code simpler. And note that those are particularly annoying error cases: As noone knows how to reach the corresponding code paths, you can’t test them, and then your line coverage suffers etc… The only inconvenient thing about my suggestion is that libsecp256k1 only provides secp256k1_ec_pubkey_tweak_add, which has the checks included… :cry: (If we were to implement this function from scratch, we’d probably ignore the error case.) But that won’t be an issue if we go down the proper road of having a libsecp256k1 module for silent payments.


    josibake commented at 4:00 pm on January 5, 2024:
    I prefer option 2 in that it lets the reader know this can happen but somewhat implies that it’s the responsibility of the crypto library being used (not the wallet) to check that the tweak was valid.

    josibake commented at 9:55 pm on January 10, 2024:
    Updated
  150. in bip-0352.mediawiki:245 in 184a48a77f outdated
    240+
    241+''' P2TR '''
    242+
    243+The sender MUST use the private key corresponding to the taproot output key (i.e. the tweaked private key for a key path spend). This can be a single private key or an aggregate key (e.g. taproot outputs using MuSig or FROST)<ref name="musig_frost_support">'''Are key aggregation techniques like FROST and MuSig supported?''' While we do not recommend it due to lack of a security proof (except if all participants are trusted or are the same entity), any taproot output able to do a key path theoretically is supported. Any offline key aggregation technique can be used, such as FROST or MuSig. This would require participants to perform the ECDH step collaboratively e.g. ''ECDH = a<sub>0</sub>·B<sub>scan</sub> + a<sub>1</sub>·B<sub>scan</sub> + ... + a<sub>t</sub>·B<sub>scan</sub>'' and ''P = B<sub>spend</sub> + hash(outpoints_hash·ECDH || 0)·G''. Additionally, it may be necessary for the participants to provide a DLEQ proof to ensure they are not acting maliciously.</ref>. If this key is not available, the output cannot be included as an input to the transaction. The receiver always uses the taproot output key when scanning, regardless of whether the taproot output is using a key path spend or a script path spend<ref name="why_always_output_pubkey">''' Why not skip all taproot script path spends? ''' This causes malleability issues for CoinJoins. If the silent payments protocol skipped taproot script path spends, this would allow an attacker to join a CoinJoin round, participate in deriving the silent payment address using the tweaked private key for a key path spend, and then broadcast their own version of the transaction using the script path spend. If the receiver were to only consider key path spends, they would skip the attacker's script path spend input when deriving the shared secret and not be able to find the funds. Additionally, there may be scenarios where a sender has access to the key path private key but spends the output using the script path.</ref>.
    244+
    245+The one exception is script path spends that use NUMS point ''H'' as their internal key (where ''H = lift_x(0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0)'' which is constructed by taking the hash of the standard uncompressed encoding of the secp256k1 base point ''G'' as X coordinate, see [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs BIP341: Constructing and spending Taproot outputs] for more details), in which case the output will be skipped for the purposes of shared secret derivation<ref name="why_ignore_h">'''Why skip outputs with H as the internal taproot key?''' If use cases get popularized where the taproot key path cannot be used, these outputs can still be included without getting in the way of making a silent payment, provided they specifically use H as their internal taproot key.</ref>.
    


    vostrnad commented at 10:46 pm on November 2, 2023:

    The internal key is not a point but a 32-byte array (optionally interpreted as a number), see discussion in #1406. This should be changed to:

    The one exception is script path spends that use the NUMS number H as their internal key (where H = 0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 which is…


    RubenSomsen commented at 1:25 pm on November 16, 2023:
    Maybe I’m missing something but that doesn’t seem right to me. H is a point on the curve and #1406 seems to corroborate this.

    vostrnad commented at 10:29 pm on November 20, 2023:

    It doesn’t corroborate it, though it doesn’t fix it either:

    it does not make sense to say “pick as internal key a point” […] But I’m not fixing this in this PR.

    BIP340 and BIP341 make a clear distinction between EC points, integers, and byte arrays, and the taproot internal key is unambiguously defined as a byte array.

    However, since BIP341 still has the same incorrect definition of H, maybe leave it as is here, perhaps someone in the future will fix them both.


    josibake commented at 12:45 pm on December 6, 2023:
    Would prefer to leave it as is here for consistency, and happy to add fixing both to my todo list :smile:
  151. in bip-0352.mediawiki:267 in 184a48a77f outdated
    262+
    263+The receiver obtains the public key from the ''scriptSig''. The receiver MUST parse the ''scriptSig'' for the public key, even if the ''scriptSig'' is non-standard (e.g. <code><dummy> OP_DROP <Signature> <Public Key></code>). This is to address the [https://en.bitcoin.it/wiki/Transaction_malleability third-party malleability of ''P2PKH'' ''scriptSigs''].
    264+
    265+''' P2PK '''
    266+
    267+The sender performs the tweak using the private key for the output and the receiver obtains the public key from the ''scriptPubKey''.
    


    vostrnad commented at 10:46 pm on November 2, 2023:
    Why was P2PK recently added as an allowed input type? Is it really worth the implementation burden when it’s so rarely used and doesn’t even have a standard address format?

    RubenSomsen commented at 1:30 pm on November 16, 2023:
    The implementation burden seems very minimal considering it’s a simple script and only concerns recipients which already rely on full nodes anyway. In terms of value a significant amount of coins (mostly early miners) is locked up in p2pk.

    Sosthene00 commented at 9:18 am on November 20, 2023:
    Sorry I missed this update, I think adding P2PK could significantly worsen the already significantly bad performance hit to blockchain indexers that would like to serve silent payments tweak data to light client, as the indexer would need to do a lookup to find the scriptpubkey and this is costly. Otoh this could probably be solved for now by discarding transactions made before silent payments existed, since the bulk of transactions spending P2PK happened a while ago. Still better to be aware that it’s not costless.

    RubenSomsen commented at 12:06 pm on November 20, 2023:

    @Sosthene00 I’m afraid that the input scripts need to be checked regardless. Of course it’s needed for all taproot inputs (which will already far outweigh the number of p2pk inputs), but perhaps more importantly, it is unsafe to assume you know what the input script will look like based solely on the output script. For instance, the output script may look like p2wpkh but the input script is actually segwit v2 (as of yet undefined, so the script succeeds regardless of what the output script might look like).

    I am unsure what exact implementation you had in mind for creating the index, but for Bitcoin Core we are reading the undo data to uncover the input scripts for each block, which is quite efficient. Perhaps you could consider something similar.


    Sosthene00 commented at 3:45 pm on November 21, 2023:

    That’s actually an argument for having this index constructed on Bitcoin Core directly, what I’ve done so far is modifying electrs to construct this index and even if my code is probably rough on the edges and not the most efficient the performance hit is bad, a full sync of the signet blockchain takes about 10 times longer than without indexing the silent payments tweaks. And I’m only requesting the scriptpubkey of the prevouts from Core for inputs that spend (or seem to spend) p2tr outputs.

    I agree with you that without requesting the actual scriptpubkey I’m mostly guessing the script type, but requesting it from Core every time would probably make the performance even more abysmal and simply impractical for production. So either :

    1. tweaks index is made and served directly from Core
    2. we build some ad hoc indexer
    3. there’s some other existing indexer out there that is a lot faster than electrs and that could do it (I personnaly won’t bet too much on that)

    RubenSomsen commented at 4:30 pm on November 21, 2023:
    @Sosthene00 Yeah that all makes sense to me. It seems hard to make it performant without leaning a bit more on the full node side of things. @Sjors has been exploring building an index for Core.

    Sosthene00 commented at 5:11 pm on November 21, 2023:
    Yes I was skeptical of burdening Core with that and thought that some implementation of electrum might be a better fit for that job, but now it seems that’s a dead end and that we would still be better off doing it in Core

    josibake commented at 11:18 am on January 9, 2024:

    I’ve been going back through and trying to re-write this section with script templates (instead of the the more open ended version we have now), and I think we actually should drop P2PK. Doing so would allow us to get rid of hybrid keys and uncompressed public keys altogether by having our script templates only allow for compressed public keys.

    With P2PK, 99% of the value is in uncompressed keys. Furthermore, while there is old value locked, this type of output (and uncompressed keys in general) is almost never used anymore.

    For the value that is locked in these coins, if they wanted to use silent payments to move these coins, it would be as simple as adding a single silent payments eligible output to the transaction inputs, as part of a one time move. This seems fine to me since we don’t want to encourage using these types of outputs going forward (and the silent payments protocol dictates they would have to move these coins to a P2TR output, anyways).

  152. in bip-0352.mediawiki:285 in 184a48a77f outdated
    280+
    281+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:
    282+
    283+* Generate the ''outpoints_hash'' using all of the inputs to the transaction, using the method described above
    284+* Collect the private keys for each input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    285+* For each private key ''a<sub>i</sub>'' corresponding to a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot output, check that the private key produces a point with an even y-value and negate the private key if not<ref name="why_negate_taproot_private_keys">'''Why do taproot private keys need to be checked?''' Recall from [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] that each x-only public key has two corresponding private keys, ''d'' and ''n - d''. To maintain parity between sender and receiver, it is necessary to use the private key corresponding to the even y-value when performing the ECDH step since the receiver will assume the even y-value when summing the taproot x-only public keys.</ref>
    


    vostrnad commented at 10:46 pm on November 2, 2023:
    “X-only” seems preferred to “x-only” (it’s what BIP340 uses).
  153. in bip-0352.mediawiki:293 in 184a48a77f outdated
    288+* For each group:
    289+** Let ''ecdh_shared_secret = outpoints_hash·a·B<sub>scan</sub>''
    290+** Let ''k = 0''
    291+** For each ''B<sub>m</sub>'' in the group:
    292+*** Let ''t<sub>k</sub> = sha256(ser<sub>P</sub>(ecdh_shared_secret) || ser<sub>32</sub>(k))''
    293+**** If ''t<sub>k</sub>'' is 0 or &gt; ''n'', fail
    


    vostrnad commented at 10:47 pm on November 2, 2023:

    The condition should be “greater than or equal to”:

    0**** If ''t<sub>k</sub>'' is 0 or ≥ ''n'', fail
    

    Additionally, I would write this out a bit more verbosely, i.e. “If tk = 0 or tk ≥ n, fail.”

    Same applies to the similar line under “Scanning”.

  154. in bip-0352.mediawiki:311 in 184a48a77f outdated
    306+A scan and spend key pair using BIP32 derivation are defined (taking inspiration from [https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki BIP44]) in the following manner:
    307+
    308+     scan_private_key: m / purpose' / coin_type' / account' / 1' / 0
    309+    spend_private_key: m / purpose' / coin_type' / account' / 0' / 0
    310+
    311+`purpose` is a constant set to ''352'' following the BIP43 recommendation. Refer to [https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki BIP43] and [https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki BIP44] for more details.
    


    vostrnad commented at 10:47 pm on November 2, 2023:
    0<code>purpose</code> is a constant set to ''352'' following the BIP43 recommendation. Refer to [https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki BIP43] and [https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki BIP44] for more details.
    
  155. in bip-0352.mediawiki:334 in 184a48a77f outdated
    329+***** Remove ''output'' from ''outputs_to_check'' and rescan ''outputs_to_check'' with ''k++''
    330+**** Else, if the wallet has precomputed labels (should be done at least once for the change label where ''m = 0'')<ref name="precompute_labels">''' Why precompute labels?''' Precomputing the labels is not strictly necessary: a wallet could track the max number of labels it has used (call it ''M'') and scan for labels by adding ''hash(b<sub>scan</sub> || m)·G'' to ''P<sub>0</sub>'' for each label ''m'' up to ''M'' and comparing to the transaction outputs. This is more performant than precomputing the labels and checking via subtraction in cases where the number of eligible outputs exceeds the number of labels in use. In practice this will mainly apply to users that choose never to use labels, or users that use a single label for generating silent payment change outputs. If using a large number of labels, the wallet would need to add all possible labels to each output. This ends up being ''n·M'' additions, where ''n'' is the number of outputs in the transaction and ''M'' is the number of labels in the wallet. By precomputing the labels, the wallet only needs to compute ''hash(b<sub>scan</sub> || m)·G'' once when creating the labeled address and can determine if a label was used via a lookup, rather than adding each label to each output.</ref>:
    331+***** Compute ''hash(b<sub>scan</sub> || m)·G = output - P<sub>k</sub>''
    332+***** Check if ''hash(b<sub>scan</sub> || m)·G'' exists in the list of labels used by the wallet
    333+***** If a match is found:
    334+****** Add the ''P<sub>k</sub> + hash(b<sub>scan</sub> || m)·G'' to the wallet
    


    vostrnad commented at 10:47 pm on November 2, 2023:
    0****** Add ''P<sub>k</sub> + hash(b<sub>scan</sub> || m)·G'' to the wallet
    
  156. in bip-0352.mediawiki:341 in 184a48a77f outdated
    336+***** If the label is not found, negate ''output'' and check again
    337+*** If no matches are found, stop
    338+
    339+==== Spending ====
    340+
    341+Recall that a silent payments is of the form ''B<sub>spend</sub> + t<sub>k</sub>·G + hash(b<sub>scan</sub> || m)·G'', where ''hash(b<sub>scan</sub> || m)·G'' is an optional label. To spend a silent payments output:
    


    vostrnad commented at 10:47 pm on November 2, 2023:
    0Recall that a silent payment output is of the form ''B<sub>spend</sub> + t<sub>k</sub>·G + hash(b<sub>scan</sub> || m)·G'', where ''hash(b<sub>scan</sub> || m)·G'' is an optional label. To spend a silent payment output:
    
  157. in bip-0352.mediawiki:343 in 184a48a77f outdated
    338+
    339+==== Spending ====
    340+
    341+Recall that a silent payments is of the form ''B<sub>spend</sub> + t<sub>k</sub>·G + hash(b<sub>scan</sub> || m)·G'', where ''hash(b<sub>scan</sub> || m)·G'' is an optional label. To spend a silent payments output:
    342+
    343+* Let ''d = (b<sub>spend</sub> + t<sub>k</sub> + hash(b<sub>scan</sub> || m)) mod n'', where ''hash(b<sub>scan</sub> || m)'' is the optional label integer encoded as a 256-bit number
    


    vostrnad commented at 10:47 pm on November 2, 2023:

    Perhaps more precise:

    0* Let ''d = (b<sub>spend</sub> + t<sub>k</sub> + hash(b<sub>scan</sub> || m)) mod n'', where ''hash(b<sub>scan</sub> || m)'' is the optional label interpreted as a big-endian integer
    

    RubenSomsen commented at 1:52 pm on November 9, 2023:
    Thanks for the thorough checking and all the edits. I’ll go over them soon.
  158. vostrnad commented at 10:48 pm on November 2, 2023: contributor

    A few general comments in addition to the inline comments:

    • Inconsistent usage of sha256(x) and hash(x) (which is not defined anywhere). One option would be to use sha256 everywhere, another to use hash everywhere and add a definition under “Specification”: “hash(x) refers to SHA256(x).”
    • Inconsistent usage of “y coordinate” and “y-value” (the most common name and spelling seems to be “Y coordinate”).
  159. in bip-0352.mediawiki:70 in 184a48a77f outdated
    61+
    62+Since ''a·B == b·A'' ([https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman 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).
    63+
    64+''' Creating more than one output '''
    65+
    66+In order to allow Alice to create more than one output for Bob<ref name="why_more_than_one_output">'''Why allow for more than one output?''' Allowing Alice to break her payment to Bob into multiple amounts opens up a number of privacy improving techniques for Alice, making the transaction look like a CoinJoin or better hiding the change amount by splitting both the payment and change outputs into multiple amounts. It also allows for Alice and Carol to both have their own unique output paying Bob in the event they are in a collaborative transaction and both paying Bob's silent payment address.</ref>, we include an integer in the following manner:
    


    naumenkogs commented at 8:11 am on November 10, 2023:

    Can you make more clear that what Bob considers his outputs (k_i) is independent from output order in a transaction? It could be say {k_0; change; k_1; k_2}.

    On a second thought, have you considering something like multi-path payment in lightning? (Splitting outputs across several transactions, but allowing sender to associate them together in a way that only Bob can detect)


    RubenSomsen commented at 1:38 pm on November 16, 2023:

    Added a sentence to make output order independence a bit more clear.

    We considered a lot of different designs. It is unclear to me what you think the gain is of what you suggest, however. Linking multiple separate payments is actually something we sought to avoid for privacy, but nothing is stopping people from doing so anyway using out-of-band communication or labels. There is no efficiency gain from linking payments either, since every transaction has to be scanned regardless.


    naumenkogs commented at 12:31 pm on November 27, 2023:
    1. “Independent from what” is unclear now i think :(
    2. Yeah now i thought about it, i don’t see much benefit either.
  160. in bip-0352.mediawiki:86 in 184a48a77f outdated
    77+* If ''P<sub>1</sub>'' is not found, stop
    78+* If ''P<sub>1</sub>'' is found, continue to check for ''P<sub>2</sub>'' and so on until an additional output is not found
    79+
    80+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.
    81+
    82+''' Preventing address reuse '''
    


    naumenkogs commented at 8:17 am on November 10, 2023:
    Unclear whether this is strongly recommended as opposed to the simple case above.

    RubenSomsen commented at 1:40 pm on November 16, 2023:

    You mean whether address reuse prevention is optional or not? It is not. The Specification section should make this clear.

    We explicitly say this in the opening paragraph of the overview:

    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.

  161. in bip-0352.mediawiki:108 in 184a48a77f outdated
     98+* Let ''a = a<sub>0</sub> + a<sub>1</sub> + ... + a<sub>n</sub>''
     99+* Let ''P<sub>0</sub> = B + hash(outpoints_hash·a·B || 0)·G''
    100+
    101+''' Spend and Scan Key '''
    102+
    103+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<sub>scan</sub>, B<sub>spend</sub>)''. This allows Bob to keep ''b<sub>spend</sub>'' in offline cold storage and perform the scanning with the public key ''B<sub>spend</sub>'' and private key ''b<sub>scan</sub>''. Alice performs the tweak using both of Bob's public keys in the following manner:
    


    naumenkogs commented at 9:38 am on November 10, 2023:
    worth highlighting what will happen if keys order is flipped i think.

    RubenSomsen commented at 1:42 pm on November 16, 2023:
    I’m not sure I follow. Could you elaborate on what you mean?

    naumenkogs commented at 12:49 pm on November 27, 2023:

    What if a sender confuses the two provided public keys while constructing the destination? I think the following:

    • the receiver technically still can spend it after some bug shooting
    • this spending will be less secure (as if cold/hot wallets are flipped)

    I just expect this to be a common UX mistake… given that it’s two keys which look very similar. (I agree with the comments above — it would be really nice if a sender used just one key, while the recipient used two keys). Or maybe you can add some representation difference: “spXabcd” and “scX1234”


    RubenSomsen commented at 1:04 pm on November 30, 2023:

    This seems like a misunderstanding. There are two keys, but only one address that the recipient communicates to senders. So spXabcd contains two keys.

    Of course it’s always possible for an implementation to implement the protocol incorrectly, but we can’t guard against that other than making sure we have good test coverage.


    josibake commented at 7:17 am on December 6, 2023:
    Just wanted to add that conceptually there are two keys, but from an implementation perspective it’s a bech32m encoded address with a 66-byte date part and the protocol dictates how to interpret the first 33 bytes vs the next 33 byes. Mentioning this to make the point that I don’t think there is any opportunity for UX confusion about mixing up the keys, rather the only risk here is if wallets incorrectly implement the protocol, as @RubenSomsen mentioned.
  162. in bip-0352.mediawiki:114 in 184a48a77f outdated
    104+
    105+* Let ''P<sub>0</sub> = B<sub>spend</sub> + hash(outpoints_hash·a·B<sub>scan</sub> || 0)·G''
    106+
    107+Bob detects this payment by calculating ''P<sub>0</sub> = B<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A)·G'' with his online device and can spend from his cold storage signing device using ''(b<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A)) mod n'' as the private key.
    108+
    109+''' Labels '''
    


    naumenkogs commented at 9:56 am on November 10, 2023:
    Should advice on the lookahead value, similar to key derivation?

    RubenSomsen commented at 1:53 pm on November 16, 2023:
    This is something we’ve been thinking about. We probably need to do benchmarks to come up with a value that doesn’t significantly slow down scanning, let’s say 1 million. The assumption we could make then is that the majority of users won’t need to exceed this value and can just scan this default value (or not scan labels at all if they know for a fact that they didn’t use any). People who need to exceed the value should ideally remember by how much. We could also warn users and e.g. say “hey, you reached label 999,000 so perhaps you need to consider rescanning with a higher limit to ensure you didn’t miss out on any payments”

    naumenkogs commented at 12:52 pm on November 27, 2023:
    I don’t have a particular opinion here, except for “We probably can learn something from years of wallet UX”. I think wallets usually do hundreds by default, and then a few really nice ones offer user to extend it. I don’t really understand the cost of doing this in silent payments though.
  163. in bip-0352.mediawiki:126 in 184a48a77f outdated
    116+Alice performs the tweak as before using one of the published ''(B<sub>scan</sub>, B<sub>m</sub>)'' pairs. Bob detects the labeled payment in the following manner:
    117+
    118+* Let ''P<sub>0</sub> = B<sub>spend</sub> + hash(outpoints_hash·b<sub>scan</sub>·A || 0)·G''
    119+* Subtract ''P<sub>0</sub>'' from each of the transaction outputs and check if the remainder matches any of the labels (''hash(b<sub>scan</sub> || 1)·G'', hash(b<sub>scan</sub> || 2)·G'' etc.) that the wallet has previously used
    120+
    121+It is important to note that an outside observer can easily deduce that each published ''(B<sub>scan</sub>, B<sub>m</sub>)'' pair is owned by the same entity as each published address will have ''B<sub>scan</sub>'' 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.
    


    naumenkogs commented at 7:43 am on November 13, 2023:
    Do you have any solutions to this problem in mind? How do you think about it?

    RubenSomsen commented at 2:37 pm on November 16, 2023:

    I wouldn’t frame it as a problem but rather a limitation of the feature. It’s essentially a way for the sender and receiver to add metadata (the label) to the payment.

    If you want full unlinkability you need to generate a completely new silent payment addresses, which linearly increases the scanning burden so it scales poorly and is actively recommended against.

  164. in bip-0352.mediawiki:130 in 184a48a77f outdated
    120+
    121+It is important to note that an outside observer can easily deduce that each published ''(B<sub>scan</sub>, B<sub>m</sub>)'' pair is owned by the same entity as each published address will have ''B<sub>scan</sub>'' 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.
    122+
    123+''' Labels for change '''
    124+
    125+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 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. 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.
    


    naumenkogs commented at 7:46 am on November 13, 2023:
    1. This sets a requirement to the protocol: sender should not be able to produce B_m on behalf of Bob. Should be more clear i think.
    2. If sender was able to do this, this could have opened up cool features i think. Have you thought of this?

    RubenSomsen commented at 2:43 pm on November 16, 2023:
    1. I’m not sure exactly how we could make this more clear than it currently is. We essentially hash m together with a secret and that is what we communicate to the outside world. As long as you don’t leak the secret to the outside world then nobody can generate labels on your behalf.
    2. We originally didn’t hash a secret and just used m*G directly. This would allow anyone to generate any label they like. Ultimately there’s little additional benefit to doing this and only some potential downsides like people sending to labels you did not intend. If you want people to be able to send to multiple labels, you can always just publish those labels explicitly, rather than letting users generate them for you.

    josibake commented at 12:53 pm on December 6, 2023:
    one last thing I’d mention: any time we allow the sender to control a label or some other piece of metadata, we either make the protocol interactive or drastically increase the scanning requirement for the receiver.
  165. in bip-0352.mediawiki:182 in 184a48a77f outdated
    173+''v31'' (l) is reserved for a backwards incompatible change, if needed. For silent payments v0:
    174+
    175+* If the receiver's silent payment address version is:
    176+** ''v0'': check that the data part is exactly 66-bytes. Otherwise, fail
    177+** ''v1'' through ''v30'': read the first 66-bytes of the data part and discard the remaining bytes
    178+** ''v31'': fail
    


    naumenkogs commented at 7:49 am on November 13, 2023:
    The table above have - for v31. How is it even possible to arrive at that then?

    RubenSomsen commented at 1:54 pm on November 16, 2023:
    See the sentence right below the table. The table only contains the backwards compatible values. v31 is the incompatible value.
  166. in bip-0352.mediawiki:448 in 184a48a77f outdated
    422+
    423+== Appendix A: Light Client Support ==
    424+
    425+This section proposes a few ideas for how light clients could be supported in ways that preserve bandwidth and privacy. While this is out of scope for the current BIP, it is included to movitate 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.
    426+
    427+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.
    


    naumenkogs commented at 8:03 am on November 13, 2023:
    Unclear whether this section is about sending or receiving. A hint would help.

    RubenSomsen commented at 1:54 pm on November 16, 2023:
    Added a sentence to clarify this. Thanks for the review!
  167. in bip-0352.mediawiki:245 in c966f94bff outdated
    240+
    241+''' P2TR '''
    242+
    243+The sender MUST use the private key corresponding to the taproot output key (i.e. the tweaked private key for a key path spend). This can be a single private key or an aggregate key (e.g. taproot outputs using MuSig or FROST)<ref name="musig_frost_support">'''Are key aggregation techniques like FROST and MuSig supported?''' While we do not recommend it due to lack of a security proof (except if all participants are trusted or are the same entity), any taproot output able to do a key path theoretically is supported. Any offline key aggregation technique can be used, such as FROST or MuSig. This would require participants to perform the ECDH step collaboratively e.g. ''ECDH = a<sub>0</sub>·B<sub>scan</sub> + a<sub>1</sub>·B<sub>scan</sub> + ... + a<sub>t</sub>·B<sub>scan</sub>'' and ''P = B<sub>spend</sub> + sha256(outpoints_hash·ECDH || 0)·G''. Additionally, it may be necessary for the participants to provide a DLEQ proof to ensure they are not acting maliciously.</ref>. If this key is not available, the output cannot be included as an input to the transaction. The receiver always uses the taproot output key when scanning, regardless of whether the taproot output is using a key path spend or a script path spend<ref name="why_always_output_pubkey">''' Why not skip all taproot script path spends? ''' This causes malleability issues for CoinJoins. If the silent payments protocol skipped taproot script path spends, this would allow an attacker to join a CoinJoin round, participate in deriving the silent payment address using the tweaked private key for a key path spend, and then broadcast their own version of the transaction using the script path spend. If the receiver were to only consider key path spends, they would skip the attacker's script path spend input when deriving the shared secret and not be able to find the funds. Additionally, there may be scenarios where a sender has access to the key path private key but spends the output using the script path.</ref>.
    244+
    245+The one exception is script path spends that use NUMS point ''H'' as their internal key (where ''H = lift_x(0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0)'' which is constructed by taking the hash of the standard uncompressed encoding of the secp256k1 base point ''G'' as X coordinate, see [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs BIP341: Constructing and spending Taproot outputs] for more details), in which case the output will be skipped for the purposes of shared secret derivation<ref name="why_ignore_h">'''Why skip outputs with H as the internal taproot key?''' If use cases get popularized where the taproot key path cannot be used, these outputs can still be included without getting in the way of making a silent payment, provided they specifically use H as their internal taproot key.</ref>.
    


    vostrnad commented at 1:02 am on November 21, 2023:
    If I understand correctly, this requires all participants in a CoinJoin to reveal their internal key and taproot tweak, so that others can check the internal key is not H. If that’s correct, can this be explicitly stated?

    RubenSomsen commented at 1:20 am on November 21, 2023:
    Not quite. In order to generate a silent payment output you need the cooperation of all input owners. They’d have to perform Diffie-Hellman and send you the result (together with a DLEQ), but if their internal key is not known then they wouldn’t be able to, so the fact that they succeeded automatically tells you that their internal key is not H (or some other NUMS). If the internal key is in fact H and they want to include the input, then they do have to reveal it and then the input can be safely ignored.

    vostrnad commented at 1:37 am on November 21, 2023:

    You’re right, I didn’t realize that using any NUMS as the internal key means the sender can’t do ECDH.

    It would be better for privacy to allow any H + rG as the internal key when the input should be skipped, but I’m not sure it’s possible for the receiver to detect this tweak without too big of a performance hit.


    RubenSomsen commented at 11:02 am on November 21, 2023:

    Such a tweak would indeed be impossible to detect as that’s the point of doing it in the first place - hiding the fact that you couldn’t sign with the internal key from the outside world.

    Thanks for asking such in-depth questions, it really helps with review.


    vostrnad commented at 1:06 pm on November 21, 2023:
    The sender would of course have to communicate r to the receiver in some way so that they can verify the tweak. I initially had a scheme in mind that was inefficient, but I later realized it doesn’t actually work.

    RubenSomsen commented at 1:10 pm on November 21, 2023:
    If the sender and receiver can communicate then you no longer need silent payments and can follow a much simpler protocol. Either the recipient simply communicates a fresh key to the sender, or the sender tweaks the recipient key with some secret value and communicates this value to the recipient.

    vostrnad commented at 1:21 pm on November 21, 2023:
    By communicate I meant through the transaction itself, same as the rest of the Silent Payments protocol. As a trivial example, you could have your script begin with OP_FALSE OP_IF <r> OP_ENDIF, but that reveals r to everyone, not just the recipient. I’m not sure it’s even possible to do it privately.

    RubenSomsen commented at 1:49 pm on November 21, 2023:
    Right, yeah that’s possible in a private way but it requires Diffie-Hellman to blind the value and you end up taking up additional block space and reducing the anonymity set because the tx will stand out. We considered something along those lines as a fallback for when none of the inputs were silent payment compatible, but decided against it for those reasons.
  168. in bip-0352.mediawiki:36 in c966f94bff outdated
    31+
    32+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.
    33+
    34+== Goals ==
    35+
    36+We aim to present a protocol which satisifies the following properties:
    


    naumenkogs commented at 9:12 am on November 24, 2023:
    imho this section is more like a pitch, but everyone is already convinced so it could be dropped :) just from the editing perspective, this is a distraction. You can have one sentence saying “Silent Payments is a convenient blah-blah”.

    RubenSomsen commented at 12:56 pm on November 30, 2023:
    At the very least I don’t think leaving it in is a detriment in any way, but I personally think the section is quite useful to outline exactly what the protocol achieves and makes it easier to understand design decisions and compare the protocol to alternatives. I can see how it also sounds like a pitch, but that was not the goal.
  169. in bip-0352.mediawiki:180 in c966f94bff outdated
    175+* If the receiver's silent payment address version is:
    176+** ''v0'': check that the data part is exactly 66-bytes. Otherwise, fail
    177+** ''v1'' through ''v30'': read the first 66-bytes of the data part and discard the remaining bytes
    178+** ''v31'': fail
    179+* Receiver addresses are always [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot outputs<ref name="why_taproot">'''Why only taproot outputs?''' Providing too much optionality for the protocol makes it difficult to implement and can be at odds with the goal of providing the best privacy. Limiting to taproot outputs helps simplify the implementation significantly while also putting users in the best eventual anonymity set.</ref>
    180+* The sender should sign with one of the sighash flags ''DEFAULT'', ''ALL'', ''SINGLE'', ''NONE'' (''ANYONECANPAY'' is unsafe). It is strongly recommended implementations use ''SIGHASH_ALL'' (''SIGHASH_DEFAULT'' for taproot inputs) when possible<ref name="why_not_sighash_anyonecanpay">'''Why is it unsafe to use ''SIGHASH_ANYONECANPAY''?''' Since the output address for the receiver is derived from from the sum of the [[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]] public keys, the inputs must not change once the sender has signed the transaction. If the inputs are allowed to change after the fact, the receiver will not be able to calculate the shared secret needed to find and spend the output. It is currently an open question on how a future version of silent payments could be made to work with new sighash flags such as ''SIGHASH_GROUP'' and ''SIGHASH_ANYPREVOUT''.</ref>
    


    naumenkogs commented at 1:01 pm on November 27, 2023:
    “from from”
  170. naumenkogs commented at 1:08 pm on November 27, 2023: member
    Reviewed until === Inputs For Shared Secret Derivation ===, mostly good. Will continue soon.
  171. in bip-0352.mediawiki:336 in c966f94bff outdated
    331+***** Compute ''sha256(b<sub>scan</sub> || m)·G = output - P<sub>k</sub>''
    332+***** Check if ''sha256(b<sub>scan</sub> || m)·G'' exists in the list of labels used by the wallet
    333+***** If a match is found:
    334+****** Add ''P<sub>k</sub> + sha256(b<sub>scan</sub> || m)·G'' to the wallet
    335+****** Remove ''output'' from ''outputs_to_check'' and rescan ''outputs_to_check'' with ''k++''
    336+***** If the label is not found, negate ''output'' and check again
    


    naumenkogs commented at 9:01 am on November 28, 2023:

    this particular point comes unclear:

    • is it else to one of the above?
    • check again what?

    RubenSomsen commented at 11:19 am on December 6, 2023:
    Clarified this section a bit and added a footnote about why we negate the output, so hopefully it’s more clear now.
  172. in bip-0352.mediawiki:358 in c966f94bff outdated
    332+***** Check if ''sha256(b<sub>scan</sub> || m)·G'' exists in the list of labels used by the wallet
    333+***** If a match is found:
    334+****** Add ''P<sub>k</sub> + sha256(b<sub>scan</sub> || m)·G'' to the wallet
    335+****** Remove ''output'' from ''outputs_to_check'' and rescan ''outputs_to_check'' with ''k++''
    336+***** If the label is not found, negate ''output'' and check again
    337+*** If no matches are found, stop
    


    naumenkogs commented at 9:02 am on November 28, 2023:
    this looks like a return at the end of a function. Slightly confusing. Is it necessary?

    RubenSomsen commented at 11:20 am on December 6, 2023:
    I have no strong opinion on this, but I don’t immediately see why it would be confusing.

    naumenkogs commented at 1:07 pm on December 6, 2023:

    Stop might mean either -break: stop the loop and continue with the function (say an outside loop or the following code)

    • return: stop the entire function

    I think in this particular code this means both, but nonetheless it took some time for me to evaluate this line. So I thought it’s better be dropped if it’s not necessary.

    Feel free to ignore.

  173. in bip-0352.mediawiki:371 in c966f94bff outdated
    345+
    346+==== Backup and Recovery ====
    347+
    348+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.
    349+
    350+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.
    


    naumenkogs commented at 9:08 am on November 28, 2023:
    We have a chance to specify serialization format for labels, so that a user can carry backups from one wallet to another… is this of interest?

    josibake commented at 7:35 am on December 6, 2023:
    IIRC, there is a proposal for standardizing metadata across wallets from @craigraw ? Perhaps we can add silent payments labels to that instead? I do think specifying a label serialization is outside the scope of BIP352, so I’d prefer we propose a follow-up BIP for silent payment labels, or update any existing wallet metadata BIPs

    craigraw commented at 7:38 am on December 6, 2023:
    Indeed - see BIP329.
  174. in bip-0352.mediawiki:475 in c966f94bff outdated
    449+* The shared secret portion of the private key (i.e ''hash(ecdh_shared_secret || k)'')
    450+* The outpoint and amount (so it's immediately spendable)
    451+
    452+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.
    453+
    454+A malicious notification could potentially cause the following issues:
    


    naumenkogs commented at 10:06 am on December 4, 2023:
    If we’re doing notifications anyway, what do you think about sending merkle proofs of inclusion?

    josibake commented at 7:42 am on December 6, 2023:
    IIRC, I think this is something we discussed (cc @RubenSomsen ). I’d like to avoid fully specifying how one might do notifications here only because this section is meant more to motivate further research, rather than define how notifications would work. BIP352 works without notifications, so I’d expect notifications to be a separate BIP aimed specifically at light clients if they are deemed necessary.

    RubenSomsen commented at 11:25 am on December 6, 2023:

    Yeah, this is definitely too much for the current BIP, but it’s an interesting design space to consider. Some thoughts:

    • A merkle proof may not add much if you’re already checking block filters and downloading full blocks when you get a match
    • It forces the sender to wait for the tx to confirm prior to being able to properly notify the recipient, which may create sender side UX complications
  175. naumenkogs commented at 10:07 am on December 4, 2023: member
    I reviewed all the text part, mostly looks good except couple unresolved comments (most of my comments are addressed and can be closed).
  176. josibake commented at 1:03 pm on December 6, 2023: member

    Thanks for the review @naumenkogs !

    most of my comments are addressed and can be closed

    I went through the open comments and resolved most of them. Let me know if there any I resolved that you feel are still unaddressed

  177. in bip-0352.mediawiki:304 in cfa56e3fa5 outdated
    299+
    300+=== Receiver ===
    301+
    302+==== Key Derivation ====
    303+
    304+Two keys are needed to create a silent payments address: the spend key and the scan key. Wallet software SHOULD use BIP32 hardened derivation<ref name="bip32_derivation">'''Why use BIP32 hardened derivation?''' Using BIP32 derivation allows users to add silent payments to an existing master seed. It also ensures that a user's silent payment funds are recoverable in any BIP32/BIP43 compatible wallet. Using hardened derivation ensures that it is safe to export the scan private key without exposing the master key or spend private key.</ref> for the spend key and hash it to obtain the scan key.
    


    brandonblack commented at 7:12 pm on December 6, 2023:

    Wallet software SHOULD use BIP32 hardened derivation for the spend key

    It’s actually more important that the scan key (which might be given to a 3rd party scanning service or online computer) use hardened derivation. The spend key should never leave the signing device, so it matters less.

    and hash it to obtain the scan key.

    This seems to conflict with the following line that specifies separate derivations for the two keys.


    real-or-random commented at 1:20 pm on January 5, 2024:

    This seems to conflict with the following line that specifies separate derivations for the two keys.

    Indeed. In any case, I think the spec here should give the exact derivation method, and then the following method using BIP32 seems nice.


    josibake commented at 4:04 pm on January 5, 2024:
    This is another thing we’ve gone back and forth on, and ultimately decided on specifying BIP32 hardened derivation for the scan key, which is essential (as @brandonblack notes) so that the scan key can safely be exported to a 3rd party. I’ll update the wording to be consistent here.
  178. in bip-0352.mediawiki:191 in cfa56e3fa5 outdated
    186+
    187+* The transaction contains at least one BIP341 taproot output (note: spent transactions optionally can be skipped by only considering unspent taproot outputs)
    188+* The transaction has at least one input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    189+* The transaction does not spend an output with SegWit version > 1<ref name="skip_txs_with_unknown_prevouts">'''Why skip transactions that spend SegWit version > 1?''' Skipping transactions that spend unknown output scripts allows us to have a clean upgrade path for silent payments by avoiding the need to scan the same transaction multiple times with different rule sets. If a new SegWit version is added in the future and silent payments v1 is released with support, we would want to avoid having to first scan the transaction with the silent payment v0 rules and then again with the silent payment v1 rules. Note: this restriction only applies to the inputs of a transaction.</ref>
    190+
    191+Otherwise, skip the transaction.
    


    brandonblack commented at 7:17 pm on December 6, 2023:
    This sentence is redundant with the “if and only if” above. Not sure if you want to remove or rephrase 🤷
  179. in bip-0352.mediawiki:243 in cfa56e3fa5 outdated
    238+
    239+Since uncompressed and hybrid public keys can be used for any of the above inputs, the receiver MUST be able to parse these public key types. It is unlikely a sender would use these types of inputs when funding the transaction (and strongly recommended that they don't), but mentioned here for completeness.
    240+
    241+''' P2TR '''
    242+
    243+The sender MUST use the private key corresponding to the taproot output key (i.e. the tweaked private key for a key path spend). This can be a single private key or an aggregate key (e.g. taproot outputs using MuSig or FROST)<ref name="musig_frost_support">'''Are key aggregation techniques like FROST and MuSig supported?''' While we do not recommend it due to lack of a security proof (except if all participants are trusted or are the same entity), any taproot output able to do a key path theoretically is supported. Any offline key aggregation technique can be used, such as FROST or MuSig. This would require participants to perform the ECDH step collaboratively e.g. ''ECDH = a<sub>0</sub>·B<sub>scan</sub> + a<sub>1</sub>·B<sub>scan</sub> + ... + a<sub>t</sub>·B<sub>scan</sub>'' and ''P = B<sub>spend</sub> + sha256(outpoints_hash·ECDH || 0)·G''. Additionally, it may be necessary for the participants to provide a DLEQ proof to ensure they are not acting maliciously.</ref>. If this key is not available, the output cannot be included as an input to the transaction. The receiver always uses the taproot output key when scanning, regardless of whether the taproot output is using a key path spend or a script path spend<ref name="why_always_output_pubkey">''' Why not skip all taproot script path spends? ''' This causes malleability issues for CoinJoins. If the silent payments protocol skipped taproot script path spends, this would allow an attacker to join a CoinJoin round, participate in deriving the silent payment address using the tweaked private key for a key path spend, and then broadcast their own version of the transaction using the script path spend. If the receiver were to only consider key path spends, they would skip the attacker's script path spend input when deriving the shared secret and not be able to find the funds. Additionally, there may be scenarios where a sender has access to the key path private key but spends the output using the script path.</ref>.
    


    brandonblack commented at 7:26 pm on December 6, 2023:

    where a sender has access to the key path private key but spends

    “where the sender can perform ECDH with the key path private key but spends”?

  180. brandonblack commented at 7:43 pm on December 6, 2023: contributor

    Looks like 352 isn’t yet added to the top level README table in this PR.

    Reviewed the text, but not test vectors or reference implementation.

    One overall comment is that the real world applicability of this is currently going to be limited to single signature taproot wallets, which is understandable, but also disappointing. This limitation arises in part from the lack of a security proof (or intuition) for FROST/MuSig2 keys, from the specific derivation description for the scan and spend keys, and lack of description of how a tapscript wallet would publish their tweaked internal key in a compatible way (obviously assuming the existence of an internal key for which ECDH is implemented).

  181. in bip-0352.mediawiki:336 in cfa56e3fa5 outdated
    313+==== Scanning ====
    314+
    315+If each of the checks in ''[[#scanning-silent-payment-eligible-transactions|Scanning silent payment eligible transactions]]'' passes, the receiving wallet must:
    316+
    317+* Generate the ''outpoints_hash'', using the method described above
    318+* Let ''A = A<sub>0</sub> + A<sub>1</sub> + ... + A<sub>n</sub>'', where each ''A<sub>i</sub>'' is the public key of an input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    


    theStack commented at 8:25 pm on December 11, 2023:

    pedantic nit: shouldn’t the subscripts go from 0…(n-1), rather than 0…n, assuming n is the number of of inputs here?

    0* Let ''A = A<sub>0</sub> + A<sub>1</sub> + ... + A<sub>n-1</sub>'', where each ''A<sub>i</sub>'' is the public key of an input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    

    Same at a few other places in the BIP.


    josibake commented at 7:29 pm on January 10, 2024:
    hm, I’m actually not sure what the convention should be. n does not represent the number of inputs in this case; it represents the number of silent payments eligible inputs (which is <= the total number of inputs).

    vostrnad commented at 11:19 pm on January 10, 2024:
    Agree with the nit. The last key should either be An-1 or the keys should not be 0-indexed but 1-indexed, making the first key A1. I’m very slightly in favor of the second option, as this would leave 0-based indexing only where the actual index number matters.

    josibake commented at 11:38 am on January 15, 2024:
    Updated to use the second option.
  182. in bip-0352.mediawiki:199 in cfa56e3fa5 outdated
    194+
    195+A silent payment address is constructed in the following manner:
    196+
    197+* Let ''B<sub>scan</sub>, b<sub>scan</sub> = Receiver's scan public key and corresponding private key''
    198+* Let ''B<sub>spend</sub>, b<sub>spend</sub> = Receiver's spend public key and corresponding private key''
    199+* Let ''B<sub>m</sub> = B<sub>spend</sub> + sha256(b<sub>scan</sub> || m)·G'', where ''sha256(b<sub>scan</sub> || m)·G'' an optional integer tweak for labeling
    


    theStack commented at 11:18 pm on December 19, 2023:
    It’s not stated how the integer m is serialized here (ser_32, ser_256, something else?). Or can it even be an arbitrary byte-string? Then it would still be unclear how to serialize the special case m=0 for the change label though.

    josibake commented at 9:34 pm on January 10, 2024:
    Fixed!
  183. in bip-0352/reference.py:118 in cfa56e3fa5 outdated
    54+
    55+
    56+def create_labeled_silent_payment_address(B_scan: ECPubKey, B_spend: ECPubKey, m: bytes, hrp: str = "tsp", version: int = 0) -> str:
    57+
    58+    G = ECKey().set(1).get_pubkey()
    59+    B_m = B_spend + m * G
    


    theStack commented at 11:21 pm on December 19, 2023:
    This seems to not match the description in the BIP, where also the private scan key b_scan and hashing is involved to derive B_m?

    josibake commented at 9:34 pm on January 10, 2024:
    Updated
  184. in bip-0352.mediawiki:9 in cfa56e3fa5 outdated
    0@@ -0,0 +1,472 @@
    1+<pre>
    2+  BIP: 352
    3+  Layer: Applications
    4+  Title: Silent Payments
    5+  Author: josibake <josibake@protonmail.com>
    6+          Ruben Somsen <rsomsen@gmail.com>
    7+  Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0352
    8+  Status: Draft
    9+  Type: Informational
    


    luke-jr commented at 7:34 pm on December 26, 2023:
    I think this should be Standards Track

    josibake commented at 7:04 pm on January 10, 2024:
    Per my understanding of BIP2, it’s the BIP editor that makes the final designation before merging.
  185. josibake force-pushed on Jan 3, 2024
  186. in bip-0352.mediawiki:269 in 0985f299b3 outdated
    264+
    265+The sender and receiver MUST calculate an outpoints hash for the transaction in the following manner:
    266+
    267+* Let ''A = A<sub>0</sub> + A<sub>1</sub> + ... + A<sub>n</sub>'', where each ''A<sub>i</sub>'' is the public key of an input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
    268+* Let ''outpoint = outpoint<sub>0</sub>'',  where ''outpoint<sub>0</sub>'' is the lowest outpoint lexicographically by txid and vout used in the transaction<ref name="why_lowest_outpoint"></ref>
    269+* Let ''outpoint_hash = hash(outpoint || A)''
    


    vostrnad commented at 6:22 pm on January 3, 2024:
    1. What’s the reason for including the sum of the public keys in the hash?
    2. Since this now includes both an outpoint and input public keys, I think a better name would be something like “input nonce”.

    vostrnad commented at 6:28 pm on January 3, 2024:

    There’s really no need to have outpoint as a separate variable anymore. Also, outpoint0 might be confused with the first outpoint, how about outpointL?

    0* Let ''outpoint_hash = hash(outpoint<sub>L</sub> || A)'', where ''outpoint<sub>L</sub>'' is the lowest outpoint lexicographically by txid and vout used in the transaction<ref name="why_lowest_outpoint"></ref>
    

    josibake commented at 9:38 am on January 4, 2024:
    Thanks for the suggestion, this reads better

    josibake commented at 9:51 am on January 4, 2024:

    What’s the reason for including the sum of the public keys in the hash?

    The discussion that led to including the sum of the public keys is here. I also wrote a footnote for the overview section explaining why, but realize I forgot to link it here. I’ll add that.

    I think a better name would be something like “input nonce”.

    I like this. In the reference python, I renamed it to deterministic_nonce. Maybe we can do deterministic_input_nonce?


    vostrnad commented at 8:54 pm on January 4, 2024:
    For me “input nonce” already implies it’s deterministic, so I’d prefer either that or “deterministic nonce”. Don’t care all that much though as long as it’s not “outpoint hash”.

    josibake commented at 11:35 am on January 5, 2024:
    I think input nonce makes the most sense in this context.

    real-or-random commented at 12:53 pm on January 5, 2024:

    At the risk of bike shedding, I really don’t like the term “nonce”. Yeah, the original meaning is “number used once” and this is not wrong here, but when people hear “nonce” in the context of Bitcoin / signatures, they’ll think about the EC points in ECDSA or Schnorr sigs (or their dlogs perhaps).

    But agreed, “outpoint hash” is also misleading now after inclusion of A. Perhaps just “input hash”? Or “utxo_hash”? The latter is closer to “outpoint hash” but is probably less confusion because an “UTXO” is not a precisely defined string (as opposed to an outpoint).


    real-or-random commented at 1:17 pm on January 5, 2024:
    Also, there are currently still occurrences of the term “outpoint hash” in the doc, e.g., as a heading.

    vostrnad commented at 5:51 pm on January 5, 2024:
    “Input hash” sounds good to me if we want to avoid “nonce”.

    josibake commented at 9:38 pm on January 10, 2024:
    Updated to input hash
  187. illesdavid approved
  188. in bip-0352.mediawiki:84 in f7e1978bda outdated
    79+
    80+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.
    81+
    82+''' Preventing address reuse '''
    83+
    84+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 an input nonce in the following manner:
    


    real-or-random commented at 12:54 pm on January 5, 2024:
    0If 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<sub>i</sub>''. To prevent this, Alice should include an input nonce in the following manner:
    

    or “P_0, …, P_n” if you prefer that


    josibake commented at 5:25 pm on January 5, 2024:

    In this section, we are referring to a single output (we haven’t introduced how Alice can create multiple destinations for Bob yet).

    But maybe I’m misunderstanding your point?


    real-or-random commented at 5:37 pm on January 5, 2024:

    (we haven’t introduced how Alice can create multiple destinations for Bob yet).

    No, that is introduced in the section before (if I’m not entirely mistaken). So it has been introduced at this point.


    josibake commented at 11:07 am on January 9, 2024:
    ah, my apologies! you are correct, will fix.
  189. in bip-0352.mediawiki:98 in f7e1978bda outdated
    93+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<ref name="other_inputs">'''What about inputs without public keys?''' Inputs without public keys can still be spent in the transaction but are simply ignored in the silent payments protocol.</ref>. This significantly reduces Bob's scanning requirement, makes light client support more feasible<ref name="using_all_inputs">'''How does using all inputs help light clients?''' If Alice uses a random input for the tweak, Bob necessarily has to have access to and check all transaction inputs, which requires performing an ECC multiplication per input. If instead Alice performs the tweak with the sum of the input public keys, Bob only needs the summed 33 byte public key per transaction and only does one ECC multiplication per transaction. Bob can then use BIP158 block filters to determine if any of the outputs exist in a block and thus avoids downloading transactions which don't belong to him. It is still an open question as to how Bob can source the 33 bytes per transaction in a trustless manner, see [[#appendix-a-light-client-support|Appendix A: Light Client Support]] for more details.</ref>, 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 [https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406 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.</ref>.
    94+
    95+Alice performs the tweak with the sum of her input private keys in the following manner:
    96+
    97+* Let ''A = A<sub>0</sub> + A<sub>1</sub> + ... + A<sub>n</sub>''
    98+* Let ''input_nonce = hash(txid<sub>0</sub> || vout<sub>0</sub> || A)'', where ''txid<sub>0</sub> || vout<sub>0</sub>'' is the lowest outpoint lexicographically<ref name="why_lowest_outpoint">'''Why use the lexicographically lowest outpoint for the hash?''' Recall that the purpose of including the input nonce is so that the sender and receiver can both come up with a deterministic nonce that ensures that a unique address is generated each time, even when reusing the same scriptPubKey as an input. Choosing the lowest outpoint lexicographically satisifes this requirement, while also ensuring that the generated output is not dependent on the final ordering of inputs in the transaction. Using a single outpoint also works well with memory constrained devices (such as hardware signing devices) as it does not require the device to have the entire transaction in memory in order to generate the silent payment output.</ref>
    


    real-or-random commented at 12:55 pm on January 5, 2024:

    s/lowest/smallest (also in other places)

    I think that’s just more common language.

  190. in bip-0352.mediawiki:115 in f7e1978bda outdated
    110+''' Labels '''
    111+
    112+For a single silent payment address of the form ''(B<sub>scan</sub>, B<sub>spend</sub>)'', 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<sub>spend</sub>'' with an integer ''m'' in the following way:
    113+
    114+* Let ''B<sub>m</sub> = B<sub>spend</sub> + hash(b<sub>scan</sub> || m)·G'' where m is an incrementable integer starting from 1
    115+* Publish ''(B<sub>scan</sub>, B<sub>0</sub>)'', ''(B<sub>scan</sub>, B<sub>1</sub>)'' etc.
    


    real-or-random commented at 12:57 pm on January 5, 2024:
    You don’t want to publish $B_0$ (reserved for change).
  191. in bip-0352.mediawiki:130 in f7e1978bda outdated
    121+
    122+It is important to note that an outside observer can easily deduce that each published ''(B<sub>scan</sub>, B<sub>m</sub>)'' pair is owned by the same entity as each published address will have ''B<sub>scan</sub>'' 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.
    123+
    124+''' Labels for change '''
    125+
    126+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 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. 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.
    


    real-or-random commented at 1:02 pm on January 5, 2024:

    Reserving just a single m=0 may be a bit restricting. I’m not an expert on wallets, but I can imagine that there are reasons in practice for needing more than one, and then wallets need to invent their own convention. ~You could, e.g., reserve positive scalars (or “lower half”) for normal outputs and negative scalars (upper half) for the change. Or reserve all m whose 256-bit representation starts with a 1 for change.~

    edit: Sorry, I forgot that m is anyway inside a hash. Then, of course, many other encodings are possible, e.g., you could change have “0 || m” and “1 || m”, or a different hash tag.


    josibake commented at 3:53 pm on January 5, 2024:
    It’s even simpler than that: m=0 is used to calculate Bspend0, and then the sender can generate as many outputs as they need for change using the method described in “Creating more than one output” (i.e. hash(input_nonce * a * Bscan || k), incrementing k for each desired output)

    real-or-random commented at 5:17 pm on January 5, 2024:
    Hm, I see, and I guess that you’ll want more than one “class” of change is too much of a stretch. Nevermind.

    josibake commented at 5:24 pm on January 5, 2024:
    I think so, but as you mentioned, since m is inside the hash, nothing is stopping developers from proposing a more advanced scheme, but I think it’s somewhat impossible to define that here in a way that ensures compatibility between wallets.
  192. in bip-0352.mediawiki:138 in f7e1978bda outdated
    134+* The constant ''n'' refers to the secp256k1 curve order, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141''.
    135+* ''outpoint'' (36 bytes): the <code>COutPoint</code> of an input (32-byte txid, least significant byte first || 4-byte vout, least significant byte first)<ref name="why_little_endian">'''Why are outpoints little-endian?''' Despite using big endian throughout the rest of the BIP, outpoints are sorted and hashed matching their transaction serialization, which is little-endian. This allows a wallet to parse a serialized transaction for use in silent payments without needing to re-order the bytes when computing the input nonce. Note: despite outpoints being stored and serialized as little-endian, the transaction hash (txid) is always displayed as big-endian.</ref>
    136+* ser<sub>32</sub>(i): serializes a 32-bit unsigned integer ''i'' as a 4-byte sequence, most significant byte first.
    137+* ser<sub>256</sub>(p): serializes the integer p as a 32-byte sequence, most significant byte first.
    138+* ser<sub>P</sub>(P): serializes the coordinate pair P = (x,y) as a byte sequence using SEC1's compressed form: (0x02 or 0x03) || ser<sub>256</sub>(x), where the header byte depends on the parity of the omitted Y coordinate.
    139+* hash(x): refers to SHA256(x)
    


    real-or-random commented at 1:05 pm on January 5, 2024:
    I think you should use tagged hashes (as defined in BIP340) everywhere, and define tags for every different use of a hash here. This is just good practice and ensures that hash values created for different purposes won’t be confused.

    josibake commented at 11:39 am on January 15, 2024:
    Updated to use tagged hashes, i.e. BIP0352/Label, BIP0352/Inputs, and BIP0352/SharedSecret
  193. josibake force-pushed on Jan 10, 2024
  194. josibake commented at 10:02 pm on January 10, 2024: member

    Thanks again to everyone for the thorough review! I’ve tried to address all the unaddressed comments, but please let me know if I’ve missed anything.

    Major changes:

    • Hashing has been updated to hash the smallest output || sum of the silent payment eligible input pubkeys
    • Labels are generated using the hash of the scan private key || an integer (updated in the reference python, as well)
    • Removed P2PK, uncompressed pubkeys, hybrid keys
    • Updated the inputs for shared secret derivation to include script templates
    • Wording / notation fixups

    I think the only unaddressed comment still on my todo list is to add tagged hashes

  195. josibake commented at 10:18 pm on January 10, 2024: member

    One overall comment is that the real world applicability of this is currently going to be limited to single signature taproot wallets, which is understandable, but also disappointing. This limitation arises in part from the lack of a security proof (or intuition) for FROST/MuSig2 keys, from the specific derivation description for the scan and spend keys, and lack of description of how a tapscript wallet would publish their tweaked internal key in a compatible way (obviously assuming the existence of an internal key for which ECDH is implemented).

    I’m not sure I understand some of the specific points here. I agree that FROST/Musig and collaborative usage (e.g. CoinJoin) are areas of open research, but we’ve also tried to make sure they are supported usecases as the BIP is currently written one we’ve had more time to evaluate the security.

    Regarding the spend and scan key derivations, I think we do need to make some sort of recommendation here to ensure compatibility between existing wallets, but I fully expect we will need to updated this recommendation or propose a new wallet BIP for defining standards around FROST/Musig, exporting the scan key, etc.

  196. josibake force-pushed on Jan 10, 2024
  197. bitcoin deleted a comment on Jan 10, 2024
  198. bitcoin deleted a comment on Jan 10, 2024
  199. in bip-0352.mediawiki:98 in 21656061b8 outdated
    93+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<ref name="other_inputs">'''What about inputs without public keys?''' Inputs without public keys can still be spent in the transaction but are simply ignored in the silent payments protocol.</ref>. This significantly reduces Bob's scanning requirement, makes light client support more feasible<ref name="using_all_inputs">'''How does using all inputs help light clients?''' If Alice uses a random input for the tweak, Bob necessarily has to have access to and check all transaction inputs, which requires performing an ECC multiplication per input. If instead Alice performs the tweak with the sum of the input public keys, Bob only needs the summed 33 byte public key per transaction and only does one ECC multiplication per transaction. Bob can then use BIP158 block filters to determine if any of the outputs exist in a block and thus avoids downloading transactions which don't belong to him. It is still an open question as to how Bob can source the 33 bytes per transaction in a trustless manner, see [[#appendix-a-light-client-support|Appendix A: Light Client Support]] for more details.</ref>, 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 [https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406 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.</ref>.
    94+
    95+Alice performs the tweak with the sum of her input private keys in the following manner:
    96+
    97+* Let ''A = A<sub>0</sub> + A<sub>1</sub> + ... + A<sub>n</sub>''
    98+* Let ''input_hash = hash(txid<sub>0</sub> || vout<sub>0</sub> || A)'', where ''txid<sub>0</sub> || vout<sub>0</sub>'' is the smallest outpoint lexicographically<ref name="why_smallest_outpoint">'''Why use the lexicographically smallest outpoint for the hash?''' Recall that the purpose of including the input hash is so that the sender and receiver can both come up with a deterministic nonce that ensures that a unique address is generated each time, even when reusing the same scriptPubKey as an input. Choosing the smallest outpoint lexicographically satisifes this requirement, while also ensuring that the generated output is not dependent on the final ordering of inputs in the transaction. Using a single outpoint also works well with memory constrained devices (such as hardware signing devices) as it does not require the device to have the entire transaction in memory in order to generate the silent payment output.</ref>
    


    vostrnad commented at 10:56 pm on January 10, 2024:
    0* Let ''input_hash = hash(outpoint<sub>L</sub> || A)'', where ''outpoint<sub>L</sub>'' is the smallest outpoint lexicographically<ref name="why_smallest_outpoint">'''Why use the lexicographically smallest outpoint for the hash?''' Recall that the purpose of including the input hash is so that the sender and receiver can both come up with a deterministic nonce that ensures that a unique address is generated each time, even when reusing the same scriptPubKey as an input. Choosing the smallest outpoint lexicographically satisifes this requirement, while also ensuring that the generated output is not dependent on the final ordering of inputs in the transaction. Using a single outpoint also works well with memory constrained devices (such as hardware signing devices) as it does not require the device to have the entire transaction in memory in order to generate the silent payment output.</ref>
    

    Sosthene00 commented at 9:13 am on January 11, 2024:

    I have a small question about this change: if I’m not mistaken, it means that while in the previous scheme as a sender I could never add inputs without having to compute again the silent payment outputs, I now could add new inputs without altering the outputs I already computed, as long as I :

    • spend UTXOs that are not eligible for silent payments
    • make sure that the outpoint I’m adding is lexicographically not the smallest

    For example, I first craft my transaction with 1 input spending a p2tr UTXO, compute 1 silent output, and then later add 1 input spending a p2wsh, lexicographically bigger, output, and another not-sp output, I don’t need to compute the key for the 1st output again, do I?


    josibake commented at 12:53 pm on January 11, 2024:
    That’s correct: so long as the outpoint is larger than the smallest outpoint and the new input is not used for shared secret derivation, the silent payment output would stay the same.

    Sosthene00 commented at 6:21 am on January 13, 2024:
    There may be use cases for that actually

    RubenSomsen commented at 5:36 pm on January 13, 2024:
    Can you come up with an example? Input side fee bumping?

    Sosthene00 commented at 9:48 am on January 18, 2024:
    I need to think about it, maybe it makes sense if you want to add inputs from another non sp wallet? You first produce a psbt with your sp-compatible input and all your sp outputs, and use another wallet you have to add some utxos? In practice it would be tricky though. Still good to know that you have some malleability now vs absolutely none in previous version.
  200. in bip-0352.mediawiki:260 in 21656061b8 outdated
    255+    witness:      <signature> <33-byte-compressed-key>
    256+    scriptSig:    (empty)
    257+    scriptPubKey: 0 <20-byte-key-hash>
    258+                  (0x0014{20-byte-key-hash})
    259+
    260+The sender performs the tweak using the private key for the output and the receiver obtains the public key from the ''witness''.
    


    vostrnad commented at 11:00 pm on January 10, 2024:
    0The sender performs the tweak using the private key for the output and the receiver obtains the public key as the last witness item.
    
  201. in bip-0352.mediawiki:270 in 21656061b8 outdated
    265+    scriptSig:    <0 <20-byte-key-hash>>
    266+                  (0x160014{20-byte-key-hash})
    267+    scriptPubKey: HASH160 <20-byte-script-hash> EQUAL
    268+                  (0xA914{20-byte-script-hash}87)
    269+
    270+The sender performs the tweak using the private key for the nested ''P2WPKH'' output and the receiver obtains the public key from the ''witness'' (inside the ''scriptSig'' field).
    


    vostrnad commented at 11:03 pm on January 10, 2024:

    There is no “witness” in the “scriptSig field”. This is probably better:

    0The 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.
    
  202. in bip-0352/reference.py:112 in 21656061b8 outdated
    107+    data = convertbits(B_scan.get_bytes(False) + B_m.get_bytes(False), 8, 5)
    108+    return bech32_encode(hrp, [version] + data, Encoding.BECH32M)
    109+
    110+
    111+def generate_label(b_scan: ECKey, m: int) -> bytes:
    112+    return sha256(b_scan.get_bytes() + ser_uint32(m))
    


    theStack commented at 11:47 pm on January 10, 2024:
    This new function is unused, I assume it was meant to be called from create_labeled_silent_payment_address below?

    josibake commented at 12:56 pm on January 11, 2024:
    good catch! it is meant to be used there and I am also using it when generating the test vectors.
  203. josibake force-pushed on Jan 11, 2024
  204. josibake commented at 3:42 pm on January 11, 2024: member
    Updated reference.py and labels format in test vectors
  205. Eunovo commented at 9:33 pm on January 14, 2024: contributor
    Hey @josibake, why doesn’t the scanning algorithm just keep looking for labels until it hits a point where there are no more payments? Like going through from m = 0 to n, and stopping when it doesn’t find any payments at m = n. The same method is already used for scanning P(k) but not labels.
  206. RubenSomsen commented at 0:02 am on January 15, 2024: contributor

    @Eunovo I’m not sure if I completely understood the question, but there is a negligible computational cost to checking for more than one label, because we essentially calculate the label for each output and match it with a label database (if it’s not a payment to you then there won’t be a match).

    If we scanned from 0 to n and then half the time be forced to go back and scan all over again from n+1 to p, this would be significantly slower.

    Now I do think this is reasonable if we set n so high that 99.9% of all users never hit it, but then it’s probably also reasonable to expect that those 0.01% power users know to manually set n to a higher value when scanning.

  207. josibake commented at 8:53 am on January 15, 2024: member

    Hey @josibake, why doesn’t the scanning algorithm just keep looking for labels until it hits a point where there are no more payments? Like going through from m = 0 to n, and stopping when it doesn’t find any payments at m = n. The same method is already used for scanning P(k) but not labels.

    What does n represent here? The number of outputs in the transaction? Or just some large number defined by the scanner?

  208. josibake force-pushed on Jan 15, 2024
  209. josibake force-pushed on Jan 15, 2024
  210. josibake commented at 12:13 pm on January 15, 2024: member

    Updated https://github.com/bitcoin/bips/commit/f0625f73c08030293da925090cfbe9a927980dd5 -> https://github.com/bitcoin/bips/commit/901d41fc10f66d73a17581344b4646e981a52910 (compare)

    • Split BIP text and testing vectors into two commits. I expect fewer changes to the BIP text going forward and most changes (if any) to be around updating the test vectors with new test case
    • Updated the BIP type, per @luke-jr feedback
    • Updated the notation to use 1 .. n, per @theStack , @vostrnad feedback
    • Updated the BIP/test vectors to use tagged hashes, per @real-or-random feedback
    • Updated the wording for BIP32 derivation to imply support for FROST/Musig/etc, per @brandonblack feedback
  211. josibake force-pushed on Jan 15, 2024
  212. josibake commented at 2:31 pm on January 15, 2024: member

    Updated https://github.com/bitcoin/bips/commit/901d41fc10f66d73a17581344b4646e981a52910 -> https://github.com/bitcoin/bips/commit/b3495171054784d100b1a90addef463b61c9149c (compare)

    • Updated “Test vectors” section
    • Updated test vectors, reference.py to match vin structured is in bitcoin core rpcs
  213. josibake force-pushed on Jan 16, 2024
  214. josibake commented at 5:29 pm on January 16, 2024: member

    Updated https://github.com/bitcoin/bips/commit/b3495171054784d100b1a90addef463b61c9149c -> https://github.com/bitcoin/bips/commit/d73e924a5b1e43f0e5736d568097aa9a0b8cb210 (compare)

    • Updated reference.py to check for compressed keys (per the updates to the BIP)
    • Code cleanup for reference.py (mainly adding a bitcoin_utils.py for non-silent payment specific stuff)
    • Added deterministic signatures (RFC6979) to minimize diffs as we update the unit tests in send_and_receive.json
  215. in bip-0352.mediawiki:197 in a9fce21ea7 outdated
    192+
    193+A silent payment address is constructed in the following manner:
    194+
    195+* Let ''B<sub>scan</sub>, b<sub>scan</sub> = Receiver's scan public key and corresponding private key''
    196+* Let ''B<sub>spend</sub>, b<sub>spend</sub> = Receiver's spend public key and corresponding private key''
    197+* Let ''B<sub>m</sub> = B<sub>spend</sub> + hash<sub>BIP0352/Label</sub>(ser<sub>256</sub>(b<sub>scan</sub>) || ser<sub>32</sub>(m))·G'', where ''hash<sub>BIP0352/Label</sub>(ser<sub>256</sub>(b<sub>scan</sub>) || ser<sub>32</sub>(m))·G'' an optional integer tweak for labeling
    


    theStack commented at 10:56 pm on January 23, 2024:

    nit: missing verb

    0* Let ''B<sub>m</sub> = B<sub>spend</sub> + hash<sub>BIP0352/Label</sub>(ser<sub>256</sub>(b<sub>scan</sub>) || ser<sub>32</sub>(m))·G'', where ''hash<sub>BIP0352/Label</sub>(ser<sub>256</sub>(b<sub>scan</sub>) || ser<sub>32</sub>(m))·G'' is an optional integer tweak for labeling
    
  216. in bip-0352.mediawiki:351 in a9fce21ea7 outdated
    346+***** Remove ''output'' from ''outputs_to_check'' and rescan ''outputs_to_check'' with ''k++''
    347+**** Else, check for labels (always check for the change label, i.e. ''hash<sub>BIP0352/Label</sub>(ser<sub>256</sub>(b<sub>scan</sub>) || ser<sub>32</sub>(m))'' where ''m = 0'')<ref name="precompute_labels">''' Why precompute labels?''' Precomputing the labels is not strictly necessary: a wallet could track the max number of labels it has used (call it ''M'') and scan for labels by adding ''hash(b<sub>scan</sub> || m)·G'' to ''P<sub>0</sub>'' for each label ''m'' up to ''M'' and comparing to the transaction outputs. This is more performant than precomputing the labels and checking via subtraction in cases where the number of eligible outputs exceeds the number of labels in use. In practice this will mainly apply to users that choose never to use labels, or users that use a single label for generating silent payment change outputs. If using a large number of labels, the wallet would need to add all possible labels to each output. This ends up being ''n·M'' additions, where ''n'' is the number of outputs in the transaction and ''M'' is the number of labels in the wallet. By precomputing the labels, the wallet only needs to compute ''hash(b<sub>scan</sub> || m)·G'' once when creating the labeled address and can determine if a label was used via a lookup, rather than adding each label to each output.</ref>:
    348+***** Compute ''label = output - P<sub>k</sub>''
    349+***** Check if ''label'' exists in the list of labels used by the wallet
    350+***** If a match is found:
    351+****** Add ''P<sub>k</sub> + label·G'' to the wallet
    


    theStack commented at 10:58 pm on January 23, 2024:
    0****** Add ''P<sub>k</sub> + label'' to the wallet
    

    (since label is already an EC point, defined as label = output - P_k a few lines above)

  217. in bip-0352.mediawiki:413 in d73e924a5b outdated
    408+         "expected": {
    409+             "addresses": [<array of bech32m strings, one for the silent payment address and each labeled address (if used)>],
    410+             "outputs": [
    411+                 {
    412+                     "pub_key": <hex encoded X-only public key>,
    413+                     "priv_key_teak": <hex encoded private key tweak data>.
    


    theStack commented at 11:00 pm on January 23, 2024:

    typo

    0                     "priv_key_tweak": <hex encoded private key tweak data>.
    
  218. in bip-0352/reference.py:75 in d73e924a5b outdated
    70+def get_input_hash(outpoints: List[COutPoint], sum_input_pubkeys: ECPubKey) -> bytes:
    71+    lowest_outpoint = sorted(outpoints, key=lambda outpoint: (outpoint.hash, outpoint.n))[0]
    72+    return TaggedHash("BIP0352/Inputs", lowest_outpoint.serialize() + cast(bytes, sum_input_pubkeys.get_bytes(False)))
    73+
    74+
    75+def derive_silent_payment_key_pair(seed: bytes) -> Tuple[ECKey, ECKey, ECPubKey, ECPubKey]:
    


    theStack commented at 11:23 pm on January 23, 2024:
    This function is currently unused. It might be helpful to have a test vector for the key derivation?

    josibake commented at 2:00 pm on January 25, 2024:
    will delete. testing for bip32 derivation does feel a bit out of scope for this BIP
  219. josibake force-pushed on Jan 25, 2024
  220. josibake commented at 2:14 pm on January 25, 2024: member
  221. josibake commented at 2:16 pm on January 25, 2024: member
    Addressed all the outstanding feedback (let me know if I missed anything!). @RubenSomsen and I have also done a final round of review and are considering the protocol final. @luke-jr RFM, unless there are any objections?
  222. in bip-0352.mediawiki:36 in 55b05c68b0 outdated
    31+
    32+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.
    33+
    34+== Goals ==
    35+
    36+We aim to present a protocol which satisifies the following properties:
    


    vostrnad commented at 7:47 am on January 26, 2024:
    0We aim to present a protocol which satisfies the following properties:
    
  223. in bip-0352.mediawiki:53 in 55b05c68b0 outdated
    48+* Light client/SPV wallet support
    49+* Protocol is upgradeable
    50+
    51+== Overview ==
    52+
    53+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, ''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|Specification]].
    


    vostrnad commented at 7:47 am on January 26, 2024:

    I don’t recall “secp256k1” normally being italicized (nor is it in the rest of the document):

    0We 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, ''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|Specification]].
    
  224. in bip-0352.mediawiki:228 in 55b05c68b0 outdated
    223+* ''P2SH-P2WPKH''
    224+* ''P2PKH''
    225+
    226+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.
    227+
    228+For all of the output types listed, only X-only and compressed public keys are permitted<ref name="why_only_compressed_public_keys">''' Why only compressed public keys ''' Technically speaking, you could construct non-standard P2WPKH outputs using uncompressed or hybrid public keys, but why would you (see [https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#restrictions-on-public-key-type BIP143: Restrictions on public key type])? For P2PKH, uncompressed (even more so hybrid) public keys are less common than compressed keys and generally considered to be a bad idea due to their blockspace inefficiency.</ref>.
    


    vostrnad commented at 7:47 am on January 26, 2024:

    Slight rewrite (“why would you” is not a great answer):

    0For all of the output types listed, only X-only and compressed public keys are permitted<ref name="why_only_compressed_public_keys">''' Why only compressed public keys ''' Uncompressed and hybrid public keys are less common than compressed keys and generally considered to be a bad idea due to their blockspace inefficiency. Additionally, [BIP143](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#restrictions-on-public-key-type) recommends restricting P2WPKH inputs to compressed keys as a default policy.</ref>.
    
  225. in bip-0352.mediawiki:281 in 55b05c68b0 outdated
    276+
    277+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. <code><dummy> OP_DROP <Signature> <Public Key></code>). This is to address the [https://en.bitcoin.it/wiki/Transaction_malleability third-party malleability of ''P2PKH'' ''scriptSigs''].
    278+
    279+=== Input hash ===
    280+
    281+The sender and receiver MUST calculate an outpoints hash for the transaction in the following manner:
    


    vostrnad commented at 7:48 am on January 26, 2024:
    0The sender and receiver MUST calculate an input hash for the transaction in the following manner:
    
  226. in bip-0352.mediawiki:440 in 55b05c68b0 outdated
    435+* Ensure taproot script path spends are included, using the taproot output key (unless ''H'' is used as the taproot internal key)
    436+* Ensure the scanner can extract the public key from each of the input types supported (e.g. ''P2WPKH'', ''P2SH-P2WPKH'', etc.)
    437+
    438+== Appendix A: Light Client Support ==
    439+
    440+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 movitate 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.
    


    vostrnad commented at 7:48 am on January 26, 2024:
    0This 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.
    
  227. in bip-0352.mediawiki:448 in 55b05c68b0 outdated
    443+
    444+=== Tweak Data ===
    445+
    446+Recall that a silent payment eligible transaction follows [[#scanning-silent-payment-eligible-transactions|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.
    447+
    448+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 ~450MB per month, assuming 100% taproot usage and all outputs remain unspent for > 1 month. As of today, these numbers are closer to 2kb - 10kb per block (10MB - 50MB per month)<ref name="appendix_data">''' Data for Appendix A ''' These numbers are based on data from January 2023 until June 2023 (the last 6 months of data at time time of writing). See [https://github.com/josibake/bitcoin-data-analysis/blob/main/notebooks/silent-payments-light-client-data.ipynb Silent payments light client data] for the full analysis.</ref>.
    


    vostrnad commented at 7:49 am on January 26, 2024:
    0For 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 outputs remain unspent for > 1 month. As of today, these numbers are closer to 2–10 kB per block (10–50 MB per month)<ref name="appendix_data">''' Data for Appendix A ''' These numbers are based on data from January 2023 until June 2023 (the last 6 months of data at time time of writing). See [https://github.com/josibake/bitcoin-data-analysis/blob/main/notebooks/silent-payments-light-client-data.ipynb Silent payments light client data] for the full analysis.</ref>.
    
  228. in bip-0352.mediawiki:452 in 55b05c68b0 outdated
    447+
    448+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 ~450MB per month, assuming 100% taproot usage and all outputs remain unspent for > 1 month. As of today, these numbers are closer to 2kb - 10kb per block (10MB - 50MB per month)<ref name="appendix_data">''' Data for Appendix A ''' These numbers are based on data from January 2023 until June 2023 (the last 6 months of data at time time of writing). See [https://github.com/josibake/bitcoin-data-analysis/blob/main/notebooks/silent-payments-light-client-data.ipynb Silent payments light client data] for the full analysis.</ref>.
    449+
    450+=== Transaction cut-through ===
    451+
    452+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 unspent taproot output will have spent all taproot UTXOs in 326 blocks or less<ref name="appendix_data"></ref>. This means a client which only scans once every 3 days 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 unspent taproot output as of the current block height. Assuming 100% taproot usage, a client that scans once a month would likely only need around 50MB worth of data. Based on current taproot adoption, a light client scanning once every 3 days would use roughly 15MB per month and a client scanning once per month would use less than 5MB per month.
    


    vostrnad commented at 7:49 am on January 26, 2024:
    0It 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 unspent taproot output will have spent all taproot UTXOs in 326 blocks or less<ref name="appendix_data"></ref>. This means a client which only scans once every 3 days 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 unspent taproot output as of the current block height. Assuming 100% taproot usage, a client that scans once a month would likely only need around 50 MB worth of data. Based on current taproot adoption, a light client scanning once every 3 days would use roughly 15 MB per month and a client scanning once per month would use less than 5 MB per month.
    
  229. vostrnad commented at 7:51 am on January 26, 2024: contributor
    A few more typos and style nits.
  230. in bip-0352/reference.py:3 in 55b05c68b0 outdated
    0@@ -0,0 +1,301 @@
    1+#!/usr/bin/env python3
    2+
    3+import bip32  # type: ignore
    


    theStack commented at 1:14 pm on January 26, 2024:

    nit, if you retouch:

    Since the removal of the BIP32 derivation routine (see #1458 (review) ff), this import is not needed anymore now and can also be removed (especially since it’s the only external module dependency).

  231. josibake force-pushed on Jan 26, 2024
  232. josibake commented at 4:30 pm on January 26, 2024: member

    Updated https://github.com/bitcoin/bips/pull/1458/commits/55b05c68b04d486868c90d6b372a2ac8ff8f3f79 -> https://github.com/bitcoin/bips/pull/1458/commits/a2b52fea2538f106d13098c4efde016baf72052a (compare)

    • Fixed spelling errors / improved wording (h/t @vostrnad)
    • Removed unused import from reference.py (h/t @theStack)

    Thanks for the thorough @vostrnad and @theStack, much appreciated!

  233. in bip-0352.mediawiki:228 in a2b52fea25 outdated
    223+* ''P2SH-P2WPKH''
    224+* ''P2PKH''
    225+
    226+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.
    227+
    228+For all of the output types listed, only X-only and compressed public keys are permitted<ref name="why_only_compressed_public_keys">''' Why only compressed public keys ''' Uncompressed and hybrid public keys are less common than compressed keys and generally considered to be a bad idea due to their blockspace inefficiency. Additionally, [BIP143](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#restrictions-on-public-key-type) recommends restricting P2WPKH inputs to compressed keys as a default policy.</ref>.
    


    vostrnad commented at 3:16 pm on February 5, 2024:

    Right, it’s MediaWiki, not Markdown:

    0For all of the output types listed, only X-only and compressed public keys are permitted<ref name="why_only_compressed_public_keys">''' Why only compressed public keys ''' Uncompressed and hybrid public keys are less common than compressed keys and generally considered to be a bad idea due to their blockspace inefficiency. Additionally, [https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#restrictions-on-public-key-type BIP143] recommends restricting P2WPKH inputs to compressed keys as a default policy.</ref>.
    
  234. in bip-0352.mediawiki:248 in 1e10265ae8 outdated
    243+    witness:      <optional witness items> <leaf script> <control block>
    244+    scriptSig:    (empty)
    245+    scriptPubKey: 0 <32-byte-x-only-key>
    246+                  (0x5120{32-byte-x-only-key})
    247+
    248+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)<ref name="why_always_output_pubkey">''' Why not skip all taproot script path spends? ''' This causes malleability issues for CoinJoins. If the silent payments protocol skipped taproot script path spends, this would allow an attacker to join a CoinJoin round, participate in deriving the silent payment address using the tweaked private key for a key path spend, and then broadcast their own version of the transaction using the script path spend. If the receiver were to only consider key path spends, they would skip the attacker's script path spend input when deriving the shared secret and not be able to find the funds. Additionally, there may be scenarios where the can perform ECDH with the key path private key but spends the output using the script path.</ref>.
    


    Eunovo commented at 6:55 am on February 14, 2024:
    @josibake The language here is a bit confusing to me. It says “the sender MUST use the private key corresponding to the taproot output key” Doesn’t this mean that script-path spends that don’t use the taproot tweaked key should be ignored?

    RubenSomsen commented at 2:01 pm on February 14, 2024:

    This is what is explained in the footnote. Imagine you want to send a silent payment to someone, but you’re doing so in a coinjoin (i.e. a transaction where other people are allowed to add inputs). One person uses taproot for one of their inputs, then claims they will use the taproot output key (meaning their key will be used for generating the silent payment output). Now as soon as the transaction is finalized, this person swaps out the key path for a script path (this is possible because input scripts are malleable) and this is what gets confirmed on-chain instead. If we followed your suggestion and ignored the script path spend, the silent payment would fail and the money would be lost. This is why we always use the taproot output key for silent payments, regardless of whether it’s a script path spend or not.

    In other words, we deliberately separated the choice of how taproot outputs get spent from how the silent payment output is generated to work around malleability issues.


    Eunovo commented at 4:05 pm on February 14, 2024:
    @RubenSomsen Thanks. My confusion was cleared up by @josibake . I was just confused about how the sender would have used the internal key if they did a script-path spend but @josibake clarified here that this section was referring to using the private key for the D-H step and not referring to how the sender spent their UTXO. The line from the bip says “the sender MUST use the private key” but doesn’t specify what the private key is being used for.
  235. josibake force-pushed on Feb 19, 2024
  236. josibake commented at 2:31 pm on February 19, 2024: member

    Updated https://github.com/bitcoin/bips/commit/1e10265ae8212ca27deb81a995f3abd490197c6b -> https://github.com/bitcoin/bips/commit/9912b88f474dd867f19be778b11c14969b54266b (compare)

    • Small refactors to reference.py to get rid of mypy typing errors
    • Updated reference.py and send_and_receive_test_vectors.json with new test cases (h/t @S3RK , @Eunovo for improving the test vectors)
  237. TheBlueMatt commented at 6:27 pm on February 20, 2024: contributor

    Have you considered a split-pubkey address for BIP 21/QR Codes? If you build a QR code (or human-readable payment instructions) for a silent payment, you probably want backwards-compatibility by including a normal taproot address in the URI path. Then, adding a silent payment instruction would add an extra two public keys, which seems unnecessary if we can reuse the taproot address itself as one of the public keys.

    Further, should this BIP include a defined BIP 21 tag (eg just state that silent payment instructions in BIP 21 URIs should be included in a sp value)?

  238. josibake commented at 11:51 am on February 21, 2024: member

    Have you considered a split-pubkey address for BIP 21/QR Codes?

    Yep! Looked into this awhile back and summarized my thoughts on it here: #1458 (comment)

    TLDR; Mixing a reused on-chain address with silent payment instructions would lead to transactions which use the silent payment instructions being linked back to the static URI, which goes against one of the goals of this BIP (i.e. “Transactions can’t be linked to a silent payment address by an outside observer”).

    Further, should this BIP include a defined BIP 21 tag (eg just state that silent payment instructions in BIP 21 URIs should be included in a sp value)?

    Also considered this, but from what I could tell neither bitcoin:sp1qxxx?b12=<offer> or bitcoin:?b12=<offer>?sp=sp1qxxx are valid BIP21 URIs. BIP21 also mentions that URIs should be used for one time payments (although in practice this doesn’t seem to be the case), so feels like we need to update BIP21 or define a new standard for reusable payment instructions. I’m more than happy to help with either option.

  239. TheBlueMatt commented at 10:46 pm on February 21, 2024: contributor

    TLDR; Mixing a reused on-chain address with silent payment instructions would lead to transactions which use the silent payment instructions being linked back to the static URI, which goes against one of the goals of this BIP (i.e. “Transactions can’t be linked to a silent payment address by an outside observer”).

    Ugh.

    Also considered this, but from what I could tell neither bitcoin:sp1qxxx?b12= or bitcoin:?b12=?sp=sp1qxxx are valid BIP21 URIs. BIP21 also mentions that URIs should be used for one time payments (although in practice this doesn’t seem to be the case), so feels like we need to update BIP21 or define a new standard for reusable payment instructions. I’m more than happy to help with either option.

    Yea, so I think we need to relax BIP21 in a few places to make it more future-proof, but in the mean time I don’t see why sp= can’t be defined here. BIP 21 as written would mandate bitcoin:traditionaladdress?sp=… but that’s okay, and better than not defining it anywhere at all.

  240. josibake commented at 4:48 pm on February 22, 2024: member

    Yea, so I think we need to relax BIP21 in a few places to make it more future-proof, but in the mean time I don’t see why sp= can’t be defined here. BIP 21 as written would mandate bitcoin:traditionaladdress?sp=… but that’s okay, and better than not defining it anywhere at all.

    Cool, if there is conceptual agreement that we should allow something like bitcoin:?sp=sp1qxxx?b12=<offer> for a more long-lived static URI (rather than mandating the URI always have an on-chain address fallback), then I think adding a blurb here regarding sp= makes sense. I’ll update the address encoding section with a definition.

  241. TheBlueMatt commented at 5:18 pm on February 22, 2024: contributor
    Yea, not sure if @luke-jr is okay with amending a BIP that old vs just writing a new, superseding BIP that allows such things and describes more usecases but either way no harm in defining an sp= key here, even if it gets used with a traditional address in QR codes.
  242. mplsgrant commented at 10:26 pm on February 22, 2024: none

    Footnote 15 reads:

    Additionally, there may be scenarios where the can perform ECDH with the key path private key but spends the output using the script path.

    Seems like it should read:

    Additionally, there may be scenarios where the sender can perform ECDH with the key path private key but spends the output using the script path.

  243. josibake commented at 10:36 am on February 23, 2024: member

    Footnote 15 reads:

    Additionally, there may be scenarios where the can perform ECDH with the key path private key but spends the output using the script path.

    Seems like it should read:

    Additionally, there may be scenarios where the sender can perform ECDH with the key path private key but spends the output using the script path.

    Great catch, thanks!

  244. josibake commented at 3:08 pm on February 26, 2024: member

    @TheBlueMatt thinking about this more, I don’t think an sp= extension is the right way to go here. As written, BIP21 mandates a single base58 encoded on-chain address followed by additional parameters (e.g. bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?params). In practice, however, it seems people use this as bitcoin:<anything that self-identifies as an on-chain address>?params so recommending bitcoin:sp1qxxx?params makes more sense. A silent payment address is bech32m encoded, uses the sp HRP to identify itself (making the sp= key redundent) and has a backwards compatible versioning scheme (e.g. any wallet that implements support for v0 addresses will also be able to pay future versions bitcoin:sp1pxxx, bitcoin:sp1zxxx, bitcoin:sp1rxxx etc).

    but either way no harm in defining an sp= key here, even if it gets used with a traditional address in QR codes.

    If the user is following BIP21’s recommendation to only use the URI for one-time payments this is harmless, but mixing a silent payment address with a reused on-chain address in a static URI can be harmful for the sender’s privacy (see #1458 (comment)), which is why I’d recommend against doing this. If people are intending to use BIP21 URIs as person identifiers, then bitcoin:sp1qxxx?b12=<offer> seems preferable to bitcoin:?sp=sp1qxxx?b12=<offer> and more consistent with how BIP21 is used in unified QR codes today ( bitcoin:<on-chain instructions>?<lightning instructions>). Thoughts?

  245. josibake force-pushed on Feb 26, 2024
  246. kristapsk commented at 3:40 pm on February 26, 2024: none

    @TheBlueMatt thinking about this more, I don’t think an sp= extension is the right way to go here. As written, BIP21 mandates a single base58 encoded on-chain address followed by additional parameters (e.g. bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?params). In practice, however, it seems people use this as bitcoin:<anything that self-identifies as an on-chain address>?params so recommending bitcoin:sp1qxxx?params makes more sense. A silent payment address is bech32m encoded, uses the sp HRP to identify itself (making the sp= key redundent) and has a backwards compatible versioning scheme (e.g. any wallet that implements support for v0 addresses will also be able to pay future versions bitcoin:sp1pxxx, bitcoin:sp1zxxx, bitcoin:sp1rxxx etc).

    but either way no harm in defining an sp= key here, even if it gets used with a traditional address in QR codes.

    If the user is following BIP21’s recommendation to only use the URI for one-time payments this is harmless, but mixing a silent payment address with a reused on-chain address in a static URI can be harmful for the sender’s privacy (see #1458 (comment)), which is why I’d recommend against doing this. If people are intending to use BIP21 URIs as person identifiers, then bitcoin:sp1qxxx?b12=<offer> seems preferable to bitcoin:?sp=sp1qxxx?b12=<offer> and more consistent with how BIP21 is used in unified QR codes today ( bitcoin:<on-chain instructions>?<lightning instructions>). Thoughts?

    I agree here.

  247. TheBlueMatt commented at 7:27 pm on February 26, 2024: contributor

    Right, so this is basically the previous compatibility discussion again - would users prefer to have a QR code/payment instructions that will always work, and get “upgraded” to privacy if senders also support it, or a QR code/payment instructions that will fail if the sender would fall back to traditional on-chain payments. Indeed, silent payment instructions can totally be the body of the URI, rather than being shoved in the parameters, and I don’t see any reason why that isn’t explicitly allowed, however if you want the recipient to be able to pick, you should stick with parameters.

    IMO nearly all recipients would prefer the “seamless upgrade” option, rather than hard-fails, but even if you disagree with that, I don’t think its a question that some users would strongly prefer that.

    Thus, to avoid having two separate ways to build/parse silent payment QR codes, I’d strongly prefer using the parameters, and then recipients can decide whether they want to support the fallback option or not without building two totally different URIs.

  248. RubenSomsen commented at 4:35 pm on February 28, 2024: contributor

    @TheBlueMatt I see your point, though it seems to me that your goal could be addressed much more cleanly on the side of BIP21.

    As you know, the way the URI scheme is currently being used (technically not BIP21 compliant) it’s already possible to put any address you like in bitcoin:[address]. The limitation you’re indirectly pointing to is that there is only one field, so if the recipient wanted to provide options to the sender (for backwards compatibility or otherwise) they’d have no space for it.

    While I agree that adding a new field for every possible address type is one way to solve this, the much more straightforward way to go about this is to extend BIP21 to allow for more than one address field. E.g. bitcoin:[address1]?option2=[address2]&option3=[address3] etc.

    The advantage of this approach is that there’s less of a chance of implementation splintering (e.g. some implementations may only recognize bitcoin:[SP address] and others sp=[SP address] - the exact opposite of what a standard like BIP21 is meant to prevent) and newly introduced address types won’t require a new field, so BIPs like this one won’t need to be concerned with how they’re going to fit into BIP21.

    This does mean someone would need to go and update BIP21 (seemingly long overdue anyway), but conceptually it seems like the best way forward.

  249. kristapsk commented at 6:09 pm on February 28, 2024: none

    E.g. bitcoin:[address1]?option2=[address2]&option3=[address3] etc.

    Or probably something like bitcoin:address1,address2,address3?lightning=bolt12&amount=0.001? E.g. just add support to specify multiple addresses using some separator. Then sender can choose what is the best from his perspective from the supported ones in the list. Another use case could be that receiver just specifies multiple addresses of same type that could be used either to split initial payment to multiple outputs (for privacy reasons) or to not need to reuse address if initial payment is underpaid for some reason.

  250. kristapsk commented at 6:13 pm on February 28, 2024: none
    OTOH, going parameter way for alternative addresses would make it backwards compatible. Still, could be single new parameter with a list of addresses probably.
  251. TheBlueMatt commented at 10:06 pm on February 28, 2024: contributor

    While I agree that adding a new field for every possible address type is one way to solve this, the much more straightforward way to go about this is to extend BIP21 to allow for more than one address field. E.g. bitcoin:[address1]?option2=[address2]&option3=[address3] etc.

    This is the same thing, though (with the additional ability to define redundant addresses, but I’m not sure why anyone would want that/don’t see why we should support it).

    The only difference is I’m suggesting defining keys in a standard way such that wallets can know where to look if they support one type or another.

    The advantage of this approach is that there’s less of a chance of implementation splintering (e.g. some implementations may only recognize bitcoin:[SP address] and others sp=[SP address] - the exact opposite of what a standard like BIP21 is meant to prevent) and newly introduced address types won’t require a new field, so BIPs like this one won’t need to be concerned with how they’re going to fit into BIP21.

    I don’t understand this concern. This BIP (or some BIP) needs to specify a standard place to look for a a silent payment in a URI (with the features users want, presumably including the ability to have backwards compatibility with existing wallets). If you define one, don’t worry about splintering. If you don’t define one, you better bet there will be splintering :).

  252. TheBlueMatt commented at 10:07 pm on February 28, 2024: contributor

    Or probably something like bitcoin:address1,address2,address3?lightning=bolt12&amount=0.001

    This would break backwards compatibility so isn’t an option.

  253. josibake commented at 2:34 pm on February 29, 2024: member

    This is the same thing, though (with the additional ability to define redundant addresses, but I’m not sure why anyone would want that/don’t see why we should support it).

    It’s not the same thing. Today, If I create a URI bitcoin:bc1pxxx and the sender doesn’t understand taproot, the payment will fail. Similarly, if I create bitcoin:bc1pxxx?sp=sp1qxxx and the sender doesn’t understand taproot, the payment will fail. Same is true for bc1q, although far less likely. As it stands today, BIP21 is widely used in a non-backwards compatible way so adding an sp= only for backwards compatibility reasons when it isn’t really solving the backwards compatibility problem is not compelling to me.

    You can argue that p2sh, bech32 and bech32m should have been BIP21 extension keys, but the fact that they weren’t hints this approach of defining extension keys for each new way of being paid on chain isn’t a good or scalable approach. @RubenSomsen is pointing out that if BIP21 instead had a generic optionN key (conceptually, a fallback), then it Just Works (tm) for on-chain receiving and we would have a way to create a fully backward compatible URI. This also means if we ever need to introduce a new on-chain address format, it would also Just Work (tm) without needing to define a new extension.

    With this scheme, I can:

    • Make a fully static, private URI bitcoin:sp1qxxx?b12=<>
    • If I’m less concerned about privacy, I can include a traditional on-chain address as a fallback: bitcoin:sp1qxxx?option1=bc1pxxx?b12=<>
    • If I want to get paid no matter what: bitcoin:sp1qxxx?option1=bc1pxxx&option2=bc1qxxx&option3=175xxx?b12=<>
  254. TheBlueMatt commented at 2:44 pm on February 29, 2024: contributor

    As it stands today, BIP21 is widely used in a non-backwards compatible way

    Sure, and that’s a problem we should fix, almost certainly by introducing new keys in the parameters section for new address formats, at least those that are not generally expected to be universally supported.

    so adding an sp= only for backwards compatibility reasons when it isn’t really solving the backwards compatibility problem is not compelling to me.

    Huh? Its solving the backwards compatibility problem for silent payments fully. Sure, maybe something else needs to/should have been done for taproot addresses, though at this point its kinda a moot point, its fairly broadly deployed and that ship has sailed. Just because it was done one way in the past doesn’t make for a good argument that we should do that again in the future.

    You can argue that p2sh, bech32 and bech32m should have been BIP21 extension keys, but the fact that they weren’t hints this approach of defining extension keys for each new way of being paid on chain isn’t a good or scalable approach.

    Huh? This does not follow at all. “It was done this way in the past” is not an argument.

    @RubenSomsen is pointing out that if BIP21 instead had a generic optionN key (conceptually, a fallback), then it Just Works (tm) for on-chain receiving and we would have a way to create a fully backward compatible URI. This also means if we ever need to introduce a new on-chain address format, it would also Just Work (tm) without needing to define a new extension.

    I don’t see why this is connected. You can do it in 20 different ways, or define a bunch of different possible keys, it doesn’t really matter which key you define. If you want to suggest that silent payments be included under the key “optionN” rather than “sp”, sure, I mean go for it, I find that weird but if that’s what you want, its your BIP. Either way, please write it down so that we don’t have wallets doing 20 different things.

  255. RubenSomsen commented at 3:23 pm on February 29, 2024: contributor

    @TheBlueMatt

    If you define one, don’t worry about splintering

    I am concerned about splintering exactly because we’d essentially be introducing two variants: bitcoin:[SP address] (as is essentially already the standard in practice, despite that it violates BIP21) as well as bitcoin:?sp=[SP address]. This seems like a bad idea to me.

    This BIP (or some BIP) needs to specify a standard place to look for a a silent payment in a URI

    The fact that we require every new address format to specify something like this also seems problematic to me. The solution I propose solves this once and for all, for future address formats as well as past ones that currently do not have parameters.

    In other words, this can and should be solved with a single update to BIP21 instead of trying to get every BIP with a new address format to specify new URI parameters.

  256. TheBlueMatt commented at 3:46 pm on February 29, 2024: contributor

    I am concerned about splintering exactly because we’d essentially be introducing two variants: bitcoin:[SP address] (as is essentially already the standard in practice, despite that it violates BIP21) as well as bitcoin:?sp=[SP address]. This seems like a bad idea to me.

    I’m not sure its a huge deal, as long as both are specified upfront (i.e. in this BIP) so that implementers are aware that they must support both. That said, if you prefer to just have one, just do the second one and don’t worry about the first. The second supports both the onchain falback and the no-fallback use-cases and is more generic. I don’t see a reason to not simply use that always.

    The fact that we require every new address format to specify something like this also seems problematic to me. The solution I propose solves this once and for all, for future address formats as well as past ones that currently do not have parameters.

    That address formats have to define few-character string which uniquely defines the type of address is a burden on new address formats? Come on…

    They already have to if they’re using bech32 cause they have to define the HRP, which you can basically always reuse (though we don’t want to assume they are always bech32-based).

  257. RubenSomsen commented at 4:53 pm on February 29, 2024: contributor

    just do the second one and don’t worry about the first

    This goes against how we’ve seen BIP21 being implemented in practice (i.e. as bitcoin:[any address]). Even if we don’t specify bitcoin:[SP address], doesn’t mean we won’t be seeing it.

    That address formats have to define few-character string which uniquely defines the type of address is a burden on new address formats?

    Past address formats have already failed to do this, so clearly it hasn’t been working well. And the very fact that we’re currently having this discussion further shows that this is not exactly a smooth process.

    BIP21 already severely needs updating to be more in line with the current reality anyway, this seems like a logical addition.

  258. TheBlueMatt commented at 7:46 pm on February 29, 2024: contributor

    This goes against how we’ve seen BIP21 being implemented in practice (i.e. as bitcoin:[any address]). Even if we don’t specify bitcoin:[SP address], doesn’t mean we won’t be seeing it.

    I mean its possible sure, its also possible people try to byte-swap your public keys and send to an invalid address. People can creatively mis-interpret specs in all kinds of fun ways, but if we tell them what to do and have a few wallets doing it “right” and not supporting anything “wrong”, then new developers will figure out that they’re doing it wrong real fast when they go to test it once.

    Past address formats have already failed to do this, so clearly it hasn’t been working well. And the very fact that we’re currently having this discussion further shows that this is not exactly a smooth process.

    Exactly! So lets improve the process and learn from past mistakes. Instead of not bothering to define anything remotely backwards compatible, lets use silent payments to show how to do it right in a way that lets people use new features without waiting five years for the whole ecosystem to adopt it. (or, honestly, more, taproot probably wont be usable until more than five years after activation…).

    More broadly, Bitcoin has grown up - the utter failure that is taproot address support should be an indication that we need to totally rethink this. Bitcoin is a much bigger world than it used to be, and just throwing things over the wall doesn’t work anymore - there will probably always exist senders somewhere that don’t support taproot addresses.

    BIP21 already severely needs updating to be more in line with the current reality anyway, this seems like a logical addition.

    Indeed, we need to do something, and if I had to rewrite BIP21 today I’d write explicitly that “new address formats MUST define a key and be placed in the parameters section.” I don’t see a reason why we’d prefer some kind of generic optionN vs just saying “this is type Y” - the second makes it much clearer to the reader what they’re dealing with (eg new address formats could elide the HRP if they wanted to save a bit on space in QR codes, which is often worth it), whereas the first is just an opaque “here’s a thing”, while wasting QR code space.

  259. josibake commented at 1:51 pm on March 1, 2024: member

    @TheBlueMatt @RubenSomsen @kristapsk this discussion is broader than just BIP352 and is starting to feel off-topic for this BIP. I’ve opened a delving bitcoin topic where we can continue the discussion: https://delvingbitcoin.org/t/revisiting-bip21/630

    Would love to have your input over there!

  260. TheBlueMatt commented at 2:47 pm on March 1, 2024: contributor
    I’m really not sure why this ended up being a whole discussion - a simple one-line “Silent payment instructions in bitcoin: URIs MUST be placed in the sp parameter value. Wallets supporting silent payments MUST handle bitcoin: URIs with no body section (i.e. bitcoin:?sp=…) as well as URIs with a body section containing a fallback on-chain address” would suffice :). If you really feel strongly about the option thing you’re welcome to replace sp here with option1, though I’m not at all convinced that’s a useful change.
  261. TheBlueMatt commented at 5:11 pm on March 1, 2024: contributor
    I went ahead and suggested concrete updates to BIP 21 at #1555
  262. in bip-0352.mediawiki:236 in 7099623729 outdated
    231+
    232+'' Keypath spend ''
    233+
    234+    witness:      <signature>
    235+    scriptSig:    (empty)
    236+    scriptPubKey: 0 <32-byte-x-only-key>
    


    theStack commented at 2:13 am on March 5, 2024:
    0    scriptPubKey: 1 <32-byte-x-only-key>
    
  263. in bip-0352.mediawiki:245 in 7099623729 outdated
    240+
    241+'' Script path spend ''
    242+
    243+    witness:      <optional witness items> <leaf script> <control block>
    244+    scriptSig:    (empty)
    245+    scriptPubKey: 0 <32-byte-x-only-key>
    


    theStack commented at 2:14 am on March 5, 2024:
    0    scriptPubKey: 1 <32-byte-x-only-key>
    
  264. josibake force-pushed on Mar 14, 2024
  265. josibake commented at 10:38 am on March 14, 2024: member
  266. josibake force-pushed on Mar 21, 2024
  267. josibake commented at 7:30 pm on March 21, 2024: member

    Updated https://github.com/bitcoin/bips/commit/92b12abb366a4005e85382178a47918140bd918b -> https://github.com/bitcoin/bips/commit/f6dd0672d81aa22368024c8ce7fe7e763a77a9f5 (compare):

    • Removed amount from the test vectors
    • Removed sorting from the test vectors

    Amount was not being used and the sorting step was causing unnecessary complications.

  268. mplsgrant commented at 8:13 pm on March 28, 2024: none

    When calculating input_hash, should we select the smallest outpoint from the set of all outpoints in the transaction, or should we select it from the set of associated outpoints related to the inputs we used for shared secret derivation?

    While trying to reason about this, I looked at the test vectors. Two of the tests deal with excluding inputs for shared secret derivation; however, they both exclude the lexicographically largest outpoints. That means if I believed in either “set of all” or “set of associated”, then the tests would pass, but my logic would be faulty.

    The BIP’s test vectors related to my question:

    • Skip invalid P2SH inputs
    • P2PKH and P2WPKH Uncompressed Keys are skipped
  269. Eunovo commented at 8:33 am on March 29, 2024: contributor

    While trying to reason about this, I looked at the test vectors. Two of the tests deal with excluding inputs for shared secret derivation; however, they both exclude the lexicographically largest outpoints. That means if I believed in either “set of all” or “set of associated”, then the tests would pass, but my logic would be faulty. @mplsgrant Not all outpoints can be used for shared secret derivation. The excluded inputs were not excluded because of their lexicographic positions, they were excluded because they did not meet the criteria defined in https://github.com/bitcoin/bips/blob/f6dd0672d81aa22368024c8ce7fe7e763a77a9f5/bip-0352.mediawiki#inputs-for-shared-secret-derivation

    Suppose we have a Transaction T, A is the set of inputs in T that meet the shared secret derivation criteria U is the set of all inputs in T OUTPOINT(L) is the smallest outpoint lexicographically by txid and vout in U input_hash = HASHbip0352/inputs(OUTPOINT(L) || Sum(A)), See https://github.com/bitcoin/bips/blob/f6dd0672d81aa22368024c8ce7fe7e763a77a9f5/bip-0352.mediawiki#input-hash

    That said, it might be a good idea to add vectors that use an OUTPOINT(L) that is not in Set A. WDYT? @josibake

    EDIT Sum(A) sums up the pubkeys of inputs in Set A

  270. josibake force-pushed on Apr 2, 2024
  271. josibake commented at 12:04 pm on April 2, 2024: member

    Updated https://github.com/bitcoin/bips/commit/f6dd0672d81aa22368024c8ce7fe7e763a77a9f5 -> https://github.com/bitcoin/bips/commit/9c82669f833737834fa205d49e3694218a486a00 (compare):

    • Added all possible permutations to test outputs (i.e. remove order dependence for sender tests)
    • Made smallest outpoint an excluded outpoint in “Exclude uncompressed” tests

    For implementing sending, the tests give a set of inputs and outputs to match against. However, due to hashing with the counter k, it is possible to generate different output sets for the recipient depending on which order the recipient addresses are evaluated. All of these output sets are valid, but this is particularly annoying when evaluating a sending implementation against the test vectors. To make this less cumbersome, I’ve included all possible sets.

    I also included a small change based on @mplsgrant feedback which makes it more clear that the smallest_outpoint is picked from the total set of outpoints and not from the set of “Inputs for shared secret derivation.” h/t @Eunovo for the concise explanation.

  272. in bip-0352.mediawiki:108 in 9c82669f83 outdated
    103+
    104+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<sub>scan</sub>, B<sub>spend</sub>)''. This allows Bob to keep ''b<sub>spend</sub>'' in offline cold storage and perform the scanning with the public key ''B<sub>spend</sub>'' and private key ''b<sub>scan</sub>''. Alice performs the tweak using both of Bob's public keys in the following manner:
    105+
    106+* Let ''P<sub>0</sub> = B<sub>spend</sub> + hash(input_hash·a·B<sub>scan</sub> || 0)·G''
    107+
    108+Bob detects this payment by calculating ''P<sub>0</sub> = B<sub>spend</sub> + hash(input_hash·b<sub>scan</sub>·A)·G'' with his online device and can spend from his cold storage signing device using ''(b<sub>spend</sub> + hash(input_hash·b<sub>scan</sub>·A)) mod n'' as the private key.
    


    grizznaut commented at 2:54 am on April 4, 2024:

    Shouldn’t Bob also need to concatenate the output index when scanning?

    0Bob detects this payment by calculating ''P<sub>0</sub> = B<sub>spend</sub> + hash(input_hash·b<sub>scan</sub>·A || 0)·G'' with his online device and can spend from his cold storage signing device using ''(b<sub>spend</sub> + hash(input_hash·b<sub>scan</sub>·A || 0)) mod n'' as the private key.
    

    josibake commented at 6:19 am on April 4, 2024:
    Good catch, thanks!
  273. josibake force-pushed on Apr 4, 2024
  274. CakeWallet commented at 3:24 pm on April 6, 2024: none

    @TheBlueMatt @RubenSomsen @kristapsk this discussion is broader than just BIP352 and is starting to feel off-topic for this BIP. I’ve opened a delving bitcoin topic where we can continue the discussion: https://delvingbitcoin.org/t/revisiting-bip21/630

    Would love to have your input over there!

    We have implemented Silent Payments in Cake Wallet for iOS, Android, Mac, and Linux. Would love for you guys to test it and give some feedback back.

  275. CakeWallet commented at 3:26 pm on April 6, 2024: none
    We have implemented Silent Payments in Cake Wallet for iOS, Android, Mac, and Linux. Would love for you guys to test it and give some feedback back.
  276. RubenSomsen commented at 10:13 am on April 7, 2024: contributor
    Thanks everyone for all the precious feedback. It has been humbling to see the amount of support this BIP has received. @josibake and I feel the BIP is now mature and will no longer be making breaking changes. With various implementations well underway, we propose moving the BIP status to final.
  277. jirijakes commented at 0:44 am on April 9, 2024: none

    Versions are specified as:

    If the receiver’s silent payment address version is:

    • v0: check that the data part is exactly 66-bytes. Otherwise, fail
    • v1 through v30: read the first 66-bytes of the data part and discard the remaining bytes
    • v31: fail

    In the same Versions section:

    Version is communicated through the address in the same way as Segwit addresses.

    and later in Address encoding section:

    The final address is a Bech32m [refers to BIP 350] encoding of[…]

    However, both BIP 350 and BIP 173 restrict the version numbers to range 0..16.

    BIP 350:

    The following code demonstrates the checks that need to be performed.

    0def decode(hrp, addr):
    1   […]
    2   # Witness versions are in range 0..16.
    3   if data[0] > 16:
    4       return (None, None)
    

    So while technically the encoding format could handle whole 0..31 range, the BIPs restrict it. The existing Bech32 decoders might, therefore, fail with versions higher than 16, for example, rust-bech32.

    Not sure what would be the best way to deal with it – either allow the same version range as BIP 350 or make it clear that the version range differs from BIP 350.

  278. in bip-0352/bitcoin_utils.py:1 in 57c89ae162 outdated
    0@@ -0,0 +1,162 @@
    1+import hashlib
    


    jonatack commented at 6:52 pm on April 9, 2024:

    Maybe add at the top of this file and in secp256k1.py to allow invoking these files directly.

    0+#!/usr/bin/env python3
    1+
    2 import hashlib
    3 import struct
    4 from io import BytesIO
    

    josibake commented at 8:35 am on April 26, 2024:
    Hm, these are library files and shouldn’t be run directly. Not sure if there is a standard way in python to communicate that, but I think the only file that should have a shebang is reference.py?
  279. in bip-0352/reference.py:1 in 57c89ae162 outdated
    0@@ -0,0 +1,332 @@
    1+#!/usr/bin/env python3
    


    jonatack commented at 6:57 pm on April 9, 2024:

    Is this file supposed to be run with the following invocation? If yes, perhaps document that in the file and make the file executable (permissions).

    0./reference.py send_and_receive_test_vectors.json
    

    josibake commented at 8:35 am on April 26, 2024:
    will do!
  280. in bip-0352/reference.py:303 in 57c89ae162 outdated
    302+            if (len(input_pub_keys) > 0):
    303+                A_sum = reduce(lambda x, y: x + y, input_pub_keys)
    304+                input_hash = get_input_hash([vin.outpoint for vin in vins], A_sum)
    305+                pre_computed_labels = {
    306+                    (generate_label(b_scan, label) * G).get_bytes(False).hex(): generate_label(b_scan, label).hex()
    307+                    for label in given["labels"]
    


    jirijakes commented at 1:20 pm on April 13, 2024:

    I have a suggestion to reflect “always check for the change label” in reference implementation and test vector. I believe the following changes should be enough.

    Given this, the address for change would have to be removed from the relevant test case but I believe that it would be consistent with “It is important that the wallet never hands out the label with m = 0.”

    Reference (always check for m = 0):

    0                    for label in given["labels"] + [0]
    

    And then in test data (don’t explicitly refer to label 0):

     0diff --git a/bip-0352/send_and_receive_test_vectors.json b/bip-0352/send_and_receive_test_vectors.json
     1index 5d329f7..99cc52c 100644
     2--- a/bip-0352/send_and_receive_test_vectors.json
     3+++ b/bip-0352/send_and_receive_test_vectors.json
     4@@ -1829,14 +1829,11 @@
     5                         "spend_priv_key": "b8f87388cbb41934c50daca018901b00070a5ff6cc25a7e9e716a9d5b9e4d664",
     6                         "scan_priv_key": "11b7a82e06ca2648d5fded2366478078ec4fc9dc1d8ff487518226f229d768fd"
     7                     },
     8-                    "labels": [
     9-                        0
    10-                    ]
    11+                    "labels": []
    12                 },
    13                 "expected": {
    14                     "addresses": [
    15-                        "sp1qqw6vczcfpdh5nf5y2ky99kmqae0tr30hgdfg88parz50cp80wd2wqqauj52ymtc4xdkmx3tgyhrsemg2g3303xk2gtzfy8h8ejet8fz8jcw23zua",
    16-                        "sp1qqw6vczcfpdh5nf5y2ky99kmqae0tr30hgdfg88parz50cp80wd2wqqlv6saelkk5snl4wfutyxrchpzzwm8rjp3z6q7apna59z9huq4x754e5atr"
    17+                        "sp1qqw6vczcfpdh5nf5y2ky99kmqae0tr30hgdfg88parz50cp80wd2wqqauj52ymtc4xdkmx3tgyhrsemg2g3303xk2gtzfy8h8ejet8fz8jcw23zua"
    18                     ],
    19                     "outputs": [
    20                         {
    
  281. josibake commented at 9:14 am on April 15, 2024: member

    @jirijakes thanks for the thorough review!

    So while technically the encoding format could handle whole 0..31 range, the BIPs restrict it. The existing Bech32 decoders might, therefore, fail with versions higher than 16, for example, rust-bech32.

    Not sure what would be the best way to deal with it – either allow the same version range as BIP 350 or make it clear that the version range differs from BIP 350.

    Great point, I’m leaning towards having it match the versioning restrictions of BIP350 to avoid unnecessary headache for implementers. Regardless, I think we also need to include sending to a higher version address in our test vectors.

    I have a suggestion to reflect “always check for the change label” in reference implementation and test vector. I believe the following changes should be enough.

    Given this, the address for change would have to be removed from the relevant test case but I believe that it would be consistent with “It is important that the wallet never hands out the label with m = 0.”

    Great suggestion, will add!

  282. jonatack commented at 3:46 pm on April 25, 2024: contributor
    Began an initial review a fortnight ago – a couple comments I had at the time before GitHub loses them, that may or may not be relevant.
  283. in bip-0352/bitcoin_utils.py:162 in 57c89ae162 outdated
    157+    # OP_DUP OP_HASH160 OP_PUSHBYTES_20 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG
    158+    return (spk[0] == 0x76) & (spk[1] == 0xA9) & (spk[2] == 0x14) & (spk[-2] == 0x88) & (spk[-1] == 0xAC)
    159+
    160+
    161+
    162+
    


    jonatack commented at 3:50 pm on April 25, 2024:

    nit, rm extra lines

    0@@ -156,7 +156,3 @@ def is_p2pkh(spk: bytes) -> bool:
    1         return False
    2     # OP_DUP OP_HASH160 OP_PUSHBYTES_20 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG
    3     return (spk[0] == 0x76) & (spk[1] == 0xA9) & (spk[2] == 0x14) & (spk[-2] == 0x88) & (spk[-1] == 0xAC)
    4-
    5-
    6-
    7-
    
  284. josibake force-pushed on Apr 26, 2024
  285. josibake force-pushed on Apr 27, 2024
  286. josibake force-pushed on May 1, 2024
  287. murchandamus requested review from RubenSomsen on May 2, 2024
  288. murchandamus commented at 7:01 pm on May 2, 2024: contributor
    I’m not quite sure what the status of this PR is, but since at least it looked like it had an open Change Request from Ruben, I requested that he review again.
  289. murchandamus commented at 8:15 pm on May 2, 2024: contributor

    It has been pointed out to me that @RubenSomsen suggested that the document were ready for final. I interpret this as his prior change request having been satisfied and we can disregard the open Change Request.

    However, it seems to me that the next step would be to move the proposal status from Draft to Proposed rather than Final.

  290. in bip-0352.mediawiki:11 in 36fefe5f2e outdated
     6+          Ruben Somsen <rsomsen@gmail.com>
     7+  Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0352
     8+  Status: Draft
     9+  Type: Standards Track
    10+  Created: 2023-03-09
    11+  License: BSD-2-Clause
    


    murchandamus commented at 2:05 pm on May 8, 2024:
    Please consider adding the “Post-History” header to link to the mailing list and additional fora where this proposal has been discussed.

    josibake commented at 4:17 pm on May 8, 2024:
    Added.
  291. in bip-0352.mediawiki:28 in 36fefe5f2e outdated
    23+
    24+=== Motivation ===
    25+
    26+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.
    27+
    28+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<ref name="out_of_band_notifications">'''Why not use out-of-band notifications''' Out of band notifications (e.g. using something other than the Bitcoin blockchain) have been proposed as a way of addressing the privacy and cost concerns of using the Bitcoin blockchain as a messaging layer. This, however, simply moves the privacy and cost concerns somewhere else and increases the risk of losing money due to a notification not being reliably delivered, or even censored, and makes this notification data critical for backup to recover funds.</ref>. 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.
    


    murchandamus commented at 2:07 pm on May 8, 2024:
    0However, 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<ref name="out_of_band_notifications">'''Why not use out-of-band notifications''' Out-of-band notifications (e.g. using something other than the Bitcoin blockchain) have been proposed as a way of addressing the privacy and cost concerns of using the Bitcoin blockchain as a messaging layer. This, however, simply moves the privacy and cost concerns somewhere else and increases the risk of losing money due to a notification not being reliably delivered, or even censored, and makes this notification data critical for backup to recover funds.</ref>. 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.
    
  292. in bip-0352.mediawiki:26 in 36fefe5f2e outdated
    21+
    22+This BIP is licensed under the BSD 2-clause license.
    23+
    24+=== Motivation ===
    25+
    26+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.
    


    murchandamus commented at 2:07 pm on May 8, 2024:
    0Using 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.
    
  293. in bip-0352.mediawiki:63 in 36fefe5f2e outdated
    54+
    55+''' Simple case '''
    56+
    57+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:
    58+
    59+* Let ''P = B + hash(a·B)·G''
    


    murchandamus commented at 2:14 pm on May 8, 2024:
    • Let ‘‘P = B + hash(a·B)·G’’

    Nit: I noticed that the symbol ‘’·’’ (presumably elliptic curve scalar multiplication) was not introduced in the first paragraph of Overview.


    josibake commented at 4:17 pm on May 8, 2024:
    Added.
  294. in bip-0352.mediawiki:143 in 36fefe5f2e outdated
    138+
    139+For everything not defined above, we use the notation from [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#specification BIP340]. This includes the ''hash<sub>tag</sub>(x)'' notation to refer to ''SHA256(SHA256(tag) || SHA256(tag) || x)''.
    140+
    141+=== Versions ===
    142+
    143+This document defines version 0 (''sp1q''). Version is communicated through the address in the same way as Segwit addresses. 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.
    


    murchandamus commented at 2:32 pm on May 8, 2024:

    “Segwit addresses” is a bit ambiguous. Perhaps you mention explicitly BIP-173 and bech32:

    0This document defines version 0 (''sp1q''). Version is communicated through the address in the same way as bech32 addresses (see [[https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32 BIP173]]. 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.
    
  295. in bip-0352.mediawiki:203 in 36fefe5f2e outdated
    198+** If no label is applied then ''B<sub>m</sub> = B<sub>spend</sub>''
    199+* The final address is a [https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki Bech32m] encoding of:
    200+** The human-readable part "sp" for mainnet, "tsp" for testnets (e.g.  signet, testnet)
    201+** The data-part values:
    202+*** The character "q", to represent a silent payment address of version 0
    203+*** The 66 byte concatenation of the receiver's public keys, ''ser<sub>P</sub>(B<sub>scan</sub>) || ser<sub>P</sub>(B<sub>m</sub>)''
    


    murchandamus commented at 2:37 pm on May 8, 2024:
    0*** The 66-byte concatenation of the receiver's public keys, ''ser<sub>P</sub>(B<sub>scan</sub>) || ser<sub>P</sub>(B<sub>m</sub>)''
    
  296. in bip-0352.mediawiki:215 in 36fefe5f2e outdated
    210+* version [1-2 characters]
    211+* payload, 66 bytes concatenated pubkeys [ceil(66*8/5) = 106 characters]
    212+* checksum [6 characters]
    213+
    214+
    215+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.</ref> 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 [https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#checksum-design BIP173: Checksum design] for more details).
    


    murchandamus commented at 2:39 pm on May 8, 2024:
    0For 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.</ref> 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 [https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#checksum-design BIP173: Checksum design] for more details).
    
  297. murchandamus approved
  298. murchandamus commented at 3:04 pm on May 8, 2024: contributor

    This proposal seems to be complete and ready to go. I have left a few nits, but none of these are blockers. I’m not merging immediately to give the BIP champions a chance to respond to my review.

    ACK 36fefe5f2eb359049684aa966c89045e3e17f7c8

  299. murchandamus commented at 3:06 pm on May 8, 2024: contributor
    Given that there is at least one implementation of your BIP already, I was wondering whether you have considered advancing it’s status to “Proposed”.
  300. in bip-0352.mediawiki:143 in d728518c4a outdated
    139@@ -140,7 +140,7 @@ For everything not defined above, we use the notation from [https://github.com/b
    140 
    141 === Versions ===
    142 
    143-This document defines version 0 (''sp1q''). Version is communicated through the address in the same way as Segwit addresses. 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.
    144+This document defines version 0 (''sp1q''). Version is communicated through the address in the same way as bech32 addresses (see [[https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32 BIP173]]. 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.
    


    murchandamus commented at 3:44 pm on May 8, 2024:

    I’m sorry, I was confused, this link should have used single brackets instead of double brackets.

    0This document defines version 0 (''sp1q''). Version is communicated through the address in the same way as bech32 addresses (see [https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32 BIP173]. 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.
    

    josibake commented at 4:17 pm on May 8, 2024:
    Fixed.
  301. josibake force-pushed on May 8, 2024
  302. Add BIP for Silent Payments
    Co-Authored-By: Ruben Somsen <rsomsen@gmail.com>
    96f4e5a4c4
  303. Add reference.py with test vectors
    * reference.py contains the silent payment specific code
    * secp256k1.py for doing the EC operations
    * bech32m.py contains code for encoding/decoding bech32(m) addresses
    * bitcoin_utils.py contains some helper code, not specific to silent
      payments
    * send_and_receive_test_vectors.json contains the wallet unit test
      vectors
    
    Co-Authored-By: S3RK <1466284+S3RK@users.noreply.github.com>
    Co-Authored-By: Oghenovo Usiwoma <37949128+Eunovo@users.noreply.github.com>
    Co-authored-by: S.Blagogee <34041358+setavenger@users.noreply.github.com>
    33a99a1a17
  304. Update README c2b27a0ce5
  305. Apply suggestions from code review
    Punctuation and wording improvements.
    
    Co-authored-by: Mark "Murch" Erhardt <murch@murch.one>
    0ccf42c869
  306. Add post-history ef108e0e77
  307. josibake force-pushed on May 8, 2024
  308. josibake commented at 4:19 pm on May 8, 2024: member

    Given that there is at least one implementation of your BIP already, I was wondering whether you have considered advancing it’s status to “Proposed”.

    Updated to Proposed. Thanks for the review @murchandamus , RFM!

  309. josibake force-pushed on May 8, 2024
  310. Change status to Proposed 9929215dcf
  311. Minor fixups
    - Fix link
    - Add explanation for scalar multiplication
    - Spelling error in test section
    17e1d168e8
  312. josibake force-pushed on May 8, 2024
  313. murchandamus merged this on May 8, 2024
  314. murchandamus closed this on May 8, 2024

  315. junderw commented at 11:45 am on June 3, 2024: contributor

    @josibake When thinking about potential APIs for BitcoinJS with SP, the concept of PSBTs doesn’t seem to work well with SP at all. It almost precludes the tx creation, signing, and finalizing MUST be done on the same device in one go.

    I think the SP BIP should at least mention that PSBT use should be avoided in the case of fully unsigned PSBTs or only has signatures that are SIGHASH_ANYONECANPAY. Since someone could easily modify the input set.

    In the long run, SP will require a few upgrades to PSBT one of which is backwards incompatible.

    1. Add an output data field containing SP data. I think this might require the scan-private-key, otherwise the finalizer can’t double check the validity of the output scriptPubkey.
    2. (Backwards incompatible) Update the PSBT version field and add the rule that “When finalizing the last input, finalizers MUST verify the scriptPubkey of EVERY output with silent payment output data before finalizing.”

    There are a lot of use cases for PSBT which would be dangerous to use with SP and some loose non-required “conventions.”

    I would be interested to hear your thoughts. I don’t have a clear change proposal so I couldn’t think of a PR to make, so excuse my reviving a dead PR.

  316. josibake commented at 12:00 pm on June 3, 2024: member

    Hey @junderw , we are discussing PSBT proposals on delving bitcoin and @andrewtoth has already started a draft BIP. It would be great to have your input over there!

    Regarding updates to this BIP, I think it would be more appropriate to finalize a silent payments PSBT BIP and then link to that BIP here. If we do end up needing a PSBTv3 for supporting silent payments, then we should definitely mention incompatibility with older PSBT versions here and point to the new PSBTv3 proposal.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bips. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-11-21 12:10 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me