Skip to content

Instantly share code, notes, and snippets.

@RubenSomsen
Last active May 17, 2024 10:53
Show Gist options
  • Save RubenSomsen/21c477c90c942acf45f8e8f5c1ad4fae to your computer and use it in GitHub Desktop.
Save RubenSomsen/21c477c90c942acf45f8e8f5c1ad4fae to your computer and use it in GitHub Desktop.

BIP47 Prague Discussion

This discussion took place at Pizza Day Prague 2022 after a brief discussion on Silent Payments. The main points have been summarized below.

The discussion was mainly between Alekos Filini, Martin Habovštiak, and Ruben Somsen, as well as Daniela Brozzoni, Eric Sirion, Pavol Rusnak, Salvatore Ingala, and others.

This was also posted to the the bitcoin-dev mailing list.

Improving BIP47

BIP47 requires a notification transaction prior to making payments. This transaction takes up on-chain space and can easily leak privacy if not handled with extreme caution. In practice this is quite hard.

The discussion revolved around whether we can a.) minimize the on-chain space required and b.) outsource the notification transaction so the link between the sender and recipient is no longer apparent on-chain.

BIP47 space requirements

As currently implemented, BIP47 (V1/V2) requires an input key for blinding, the blinded sender payment code in an op_return, and the recipient key in an output.

The first question that came up was whether it is necessary for the recipient to learn the payment code of the sender. The benefit is that this enables the recipient to send a notification transaction and subsequent payment to the sender, but in practice this never happens. It therefore seems acceptable to forego this requirement, as this potentially saves space. The minimum notification payload that seems required is a fresh sender key and a static recipient key.

The sender key should ideally be deterministically derived from the sender xpub based on the recipient key. If the user checks all the keys that were registered with the recipient prior to notification, it can statelessly find out whether the sender key was already previously registered. This step can be skipped, which is easier for light clients, but means the notification transaction will have to be resent if the user ever forgets they already sent a notification (such as when restoring from backup).

Outsourcing the notification

The next part of the discussion revolved around the idea of putting multiple notifications in a single transaction that can be outsourced to a third party in order to break the sender/recipient link. This third party could be paid over the Lightning Network for their services.

One idea was to use the taproot annex to insert the notification payload as (discounted) witness data. One downside with this approach is that it requires custom software for the recipient to notice the notification, since it's not tied to an easily noticeable output. The middle ground solution would be to put the sender keys there but still create an output for each recipient key.

Allowing collisions

One interesting point that came up was that you could represent the recipient key using e.g. only 4 bytes (provided you put it in the annex). This leaves a window of 1 in ~4.3 billion for a collision, but the extra work that needs to be performed when it does happen is negligible (essentially expecting a payment while there is none). This would reduce the payload from 64 bytes to 36 bytes of witness data.

While this did not come up in the discussion, it should be noted that using the annex makes the transaction non-standard. It could either be standardized as the first use case for the annex, or perhaps an alternative method should be considered.

@RubenSomsen
Copy link
Author

Instead of the annex, the person to whom the notification payloads are being outsourced could put them in a p2wsh. This would mean it takes two transactions before the data appears on-chain (so the sender has to wait for this before making the actual payment), but this causes no additional overhead if we assume this person repeatedly publishes transactions (spending the p2wsh while creating a new one).

@RubenSomsen
Copy link
Author

RubenSomsen commented Jun 11, 2022

When evaluating Silent Payments, it seems important to compare it to the best possible version of BIP47 as opposed to what is out there today.

As per this gist, this version of BIP47 (v5?) adds one or two points of complexity and removes a big one:

  • Outsourcing requires private interaction over (presumably) the Lightning Network
  • Detecting new notifications requires custom infrastructure (optional, only if recipient address outputs are avoided to save space)
  • Notification transaction no longer leaks privacy (big)

The remaining downside is that it still leaves us with an on-chain footprint, albeit a reduced one (36 bytes at best). With outsourcing in place, it may also become more viable to offload this footprint to other blockchains.

My view on Silent Payments is that its main downside – the scanning requirement – is only a downside if you're unwilling to run a full node. If you are running one, then the additional effort required is unlikely to be significant (benchmarks pending).

If you're running a light client, a protocol like BIP47 is likely to be easier, however it should be noted that the vast majority of light clients shares your xpub with a server, which ruins all privacy. For actual privacy, a BIP157/158 (compact block filters) light client is required.

The final point to consider is that the specific case of directly paying to a Silent Payment address from a coinjoin adds complexity (or alternatively a small degradation of privacy by revealing your input to the recipient). BIP47 avoids this issue.

Edit: it is now two years later and significant strides have been made to make Silent Payments light client compatible. It does use a bit more bandwidth compared to a private non-SP light client, but it's quite manageable thanks to cut-through (skipping fully spent transactions).

@alfred-hodler
Copy link

This is a discussion worth having.

One thing that concerns me is that the original spec says nothing about which script types the recipient (Bob) has to watch for. At this point there are several standard address/script types and Bob never knows which ones will be receiving a payment. One way to mitigate that is to derive all script types, which wastes resources and might prove problematic for certain light clients.

It would be a lot better if a payment code reserved a few bytes for a bit flag array where a bit determines whether a certain script type will be watched by the receiver. I don't think it's appropriate to use payment code versions to communicate which scripts are being used, like the creator continues to do in the OBPP.

@alfred-hodler
Copy link

Since BIP47 suffers from a lack of engagement and appears to be abandoned, it might be better to create a new BIP in order to attract more momentum.

@stortzm
Copy link

stortzm commented Oct 12, 2022

Since BIP47 suffers from a lack of engagement and appears to be abandoned, it might be better to create a new BIP in order to attract more momentum.

BIP47 is implemented and up and running, very far from abandoned

@RubenSomsen
Copy link
Author

BIP351 essentially implements the improvements over BIP47 from the Prague write-up with one change that is more reminiscent of Silent Payments (BIP352) - it encrypts the on-chain payment code. The consequences of this are two-fold.

On the positive side this allows the sender to create an on-chain notification without needing to worry about possibly linking the sender and recipient. In BIP47 the sender had to take care never to use funds intended for paying to make a notification in order to ensure there is no link. In the Prague write-up the notification is outsourced, ensuring the link is broken as long as the entity you outsourced it to didn't learn or divulge your information.

On the negative side this means notifications are no longer directly identifiable by the recipient. Recipients are required to receive all notifications in order to personally decrypt them. This is reminiscent to scanning in Silent Payments, except instead of scanning each transaction, you scan each notification (a lot less data).

Both the up and downside are significant. Whether this is an improvement over the original Prague write-up is debatable, but it's clearly a different set of tradeoffs.

Compared to Silent Payments senders are still required to make a notification transaction (big sender-side UX hassle and costly when on-chain fees are high), and repeat payments from the same sender are linkable by the recipient. Both have a scanning requirement, but for BIP351 it's significantly lighter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment