Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BIP 77: Payjoin Version 2 — Async Payjoin #1483

Draft
wants to merge 32 commits into
base: master
Choose a base branch
from

Conversation

DanGould
Copy link
Contributor

@DanGould DanGould commented Aug 12, 2023

This document proposes an asynchronous, backwards-compatible second version of the payjoin protocol described in BIP 78, enabling complete payjoin receiver functionality including payment output substitution with only an HTTP client rather than server. The former requirement for receivers to run HTTP servers is replaced with an untrusted third-party "payjoin directory" store-and-forward server accessed by polling clients which communicate using an asynchronous protocol and authenticated, encrypted payloads. It was originally proposed to the mailing list here.

The protocol design has received rounds of review elsewhere on the bitcoin-dev mailing list as well.

Feedback from that list post has been incorporated into this draft.

Proposing this as an Standards Track BIP to ensure wallets across the ecosystem can come to rough consensus on a single asynchronous payjoin standard and correctly implement it widely.

@luke-jr
Copy link
Member

luke-jr commented Dec 26, 2023

Let's call this BIP 77

@murchandamus
Copy link
Contributor

Hi @DanGould, the first comment on this PR seems to indicate that this proposal is still WIP. Is that an accurate understanding? If this PR is not yet ready to be merged, perhaps it should be changed to "Draft". If I misunderstood the status of this PR, please respond below so someone may review to assess whether this is ready for merge.

bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
@DanGould DanGould marked this pull request as draft May 3, 2024 02:50
Co-authored-by: thebrandonlucas <[email protected]>
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Midway through a first (non-technical) review pass. Once at the end, will read through BIP78 and then do a more technical review.

bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
bip-0077.mediawiki Outdated Show resolved Hide resolved
@DanGould
Copy link
Contributor Author

DanGould commented Jun 2, 2024

@jonatack Thank you for the review and implicit advice about how to clarify technical specifications such as this one with even small changes like using explicit subjects. Your patient, thorough contribution moves the needle toward production readiness and helps me reflect on the parts of the spec that are most lacking to correct them.

In incorporating your fresh perspective, I see a few issues remaining that I'll outline as a note for myself to correct:

  • Revise document to describe Payjoin "sessions" instead of directory "enrollment". e.g.: "The payjoin version 2 protocol uses per-[session] public keys"
  • Replacethe Authenticate: <token> messaging with a mask on eligible OHTTP relay addresses that start with "pay" domains. Avoid using identifying protocols like authentication tokens to preserve privacy. The goal is to make free access to these servers while by limiting their capability to preventing DoS vectors or misuse.
  • Either include PSBTv2 in the reference implementation or remove it from the bip77 spec

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review pass of the latest changes in 3b863a4.

Note to self: continue original review pass from line 180 to the end.


====Send Messaging====

The version 2 Original PSBT is serialized in base64 followed by the query parameter string on a new line. This plaintext string encrypted according to the HPKE using a shared secret derived from a newly generated session keypair public key combined with the receiver's subdirectory session public key. The resulting HPKE payload body is then encapsulated according to Oblivious HTTP as a POST request to the directory's OHTTP Gateway.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The version 2 Original PSBT is serialized in base64 followed by the query parameter string on a new line. This plaintext string encrypted according to the HPKE using a shared secret derived from a newly generated session keypair public key combined with the receiver's subdirectory session public key. The resulting HPKE payload body is then encapsulated according to Oblivious HTTP as a POST request to the directory's OHTTP Gateway.
The version 2 Original PSBT is serialized in base64 followed by the query parameter string on a new line. This plaintext string is encrypted according to the HPKE using a shared secret derived from a newly generated session keypair public key combined with the receiver's subdirectory session public key. The resulting HPKE payload body is then encapsulated according to Oblivious HTTP as a POST request to the directory's OHTTP Gateway.
  • missing verb? (i.e. "is")

  • "on a new line" applies to the quary param only? If yes, consider s/base64/base64,

====Receive Messaging====

The receiver sends a GET request to the path of the subdirectory followed by <code>/receive</code>. This request is encapsulated in OHTTP.
The recver then awaits an OHTTP response from the directory encapsulating a request from the sender with status code 200 OK, or sends a new OHTTP request after receiving an encapsulated 202 ACCEPTED response notifying the receiver that the directory has not yet received a request from the sender.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The recver then awaits an OHTTP response from the directory encapsulating a request from the sender with status code 200 OK, or sends a new OHTTP request after receiving an encapsulated 202 ACCEPTED response notifying the receiver that the directory has not yet received a request from the sender.
The receiver then awaits an OHTTP response from the directory encapsulating a request from the sender with status code 200 OK, or sends a new OHTTP request after receiving an encapsulated 202 ACCEPTED response notifying the receiver that the directory has not yet received a request from the sender.


The version 2 Original PSBT is serialized in base64 followed by the query parameter string on a new line. This plaintext string encrypted according to the HPKE using a shared secret derived from a newly generated session keypair public key combined with the receiver's subdirectory session public key. The resulting HPKE payload body is then encapsulated according to Oblivious HTTP as a POST request to the directory's OHTTP Gateway.

The directory's OHTTP Gateway decapsulates the OHTTP request, handles the POST request at the receiver's internal subdirectory endpoint, which stores the HPKE encrypted payload to be forwarded to the receiver. The directory's awaits a request from the receiver's to the subdirectory endpoint, encapsulates to responds with the HPKE encrypted Original PSBT payload acording to OHTTP.
Copy link
Member

@jonatack jonatack Jun 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The directory's OHTTP Gateway decapsulates the OHTTP request, handles the POST request at the receiver's internal subdirectory endpoint, which stores the HPKE encrypted payload to be forwarded to the receiver. The directory's awaits a request from the receiver's to the subdirectory endpoint, encapsulates to responds with the HPKE encrypted Original PSBT payload acording to OHTTP.
The directory's OHTTP Gateway decapsulates the OHTTP request and handles the POST request at the receiver's internal subdirectory endpoint, which stores the HPKE encrypted payload to be forwarded to the receiver. The directory's OHTTP Gateway awaits a request from the receiver to the subdirectory endpoint and responds with the HPKE encrypted Original PSBT payload acording to OHTTP.

(Not sure what the latest version of this sentence intends to say; it is a bit confusing, so my suggested edits are likely to be incorrect.)


===Sender's Payjoin PSBT checklist===

The version 2 sender's checklist is largely the same as the [[https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#senders-payjoin-proposal-checklist| the BIP 78 checklist]], with the exception that it expects all UTXO data to be filled in. BIP 78 required sender inputs UTXO data to be excluded from the PSBT which has caused many issues, as it required the sender to add them back to the Payjoin proposal PSBT. Version 2 has no such requirement.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The version 2 sender's checklist is largely the same as the [[https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#senders-payjoin-proposal-checklist| the BIP 78 checklist]], with the exception that it expects all UTXO data to be filled in. BIP 78 required sender inputs UTXO data to be excluded from the PSBT which has caused many issues, as it required the sender to add them back to the Payjoin proposal PSBT. Version 2 has no such requirement.
The version 2 sender's checklist is largely the same as the [[https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#senders-payjoin-proposal-checklist| the BIP 78 checklist]], with the exception that it expects all UTXO data to be filled in. BIP 78 required sender inputs UTXO data to be excluded from the PSBT, which has caused many issues, as it required the sender to add them back to the Payjoin proposal PSBT. Version 2 has no such requirement.

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

continue original review pass from line 180 to the end

Done (for the initial proofreading pass)


When the payjoin sender posts the original PSBT to the receiver, the sender should specify the following HTTP query string parameters:

* <code>v</code>: represents the version number of the payjoin protocol that the sender is using. This version is <code>2</code>.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest writing all 3 params (2 receiver, 1 sender) either followed by a colon, or by no colon.


===Request expiration & Original PSBT===

The directory may hold a request for an offline payjoin peer until that peer comes online. However, the BIP 78 spec recommends broadcasting request PSBTs in the case of an offline counterparty. Doing so exposes a naïve, surveillance-vulnerable transaction which payjoin intends to avoid.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: link here to where BIP 78 recommends this.


The directory may hold a request for an offline payjoin peer until that peer comes online. However, the BIP 78 spec recommends broadcasting request PSBTs in the case of an offline counterparty. Doing so exposes a naïve, surveillance-vulnerable transaction which payjoin intends to avoid.

The existing BIP 78 protocol has to be synchronous only for automated endpoints, which may be vulnerable to probing attacks. It can cover this tradeoff by demanding an Original PSBT, from which a valid payment transaction may be extracted that would not preserve privacy the same way as a payjoin. BIP 21 URIs can communicate a request expiration to alleviate both of these problems. Receivers may specify a deadline after which they will broadcast this original with a new expiration parameter <code>exp=</code>. <!-- I also like to for timeout, but it's hard to coordinate in an asynchronous way -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"I also like to for timeout" -> unclear


Hybrid Public Key Encryption is a modern web standard for secure message exchange without TLS. Since client and server agree on a configuration out of band, we can pre-define the payjoin v2 application specific configuration to use DHKEM(Secp256k1, HKDF-SHA256) and ChaCha20Poly1305 AEAD.

The cryptographic handshake is conducted in parallel to the payjoin messaging inspired by the [[http:https://www.noiseprotocol.org/noise.html#zero-rtt-and-noise-protocols| zero-RTT]] version of the [[http:https://www.noiseprotocol.org/noise.html| Noise Framework]] [[https://noiseexplorer.com/patterns/NKpsk0/| IK]] pattern. A receiver shares its public key out of band in the BIP21 URI. Static keys shared in URIs must only for a single session. The key is encoded in [[https://datatracker.ietf.org/doc/html/rfc4648#section-5| base64url]] encoding as a payjoin directory subdirectory in the <code>pj=</code> parameter.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The cryptographic handshake is conducted in parallel to the payjoin messaging inspired by the [[http://www.noiseprotocol.org/noise.html#zero-rtt-and-noise-protocols| zero-RTT]] version of the [[http://www.noiseprotocol.org/noise.html| Noise Framework]] [[https://noiseexplorer.com/patterns/NKpsk0/| IK]] pattern. A receiver shares its public key out of band in the BIP21 URI. Static keys shared in URIs must only for a single session. The key is encoded in [[https://datatracker.ietf.org/doc/html/rfc4648#section-5| base64url]] encoding as a payjoin directory subdirectory in the <code>pj=</code> parameter.
The cryptographic handshake is conducted in parallel to the payjoin messaging inspired by the [[http://www.noiseprotocol.org/noise.html#zero-rtt-and-noise-protocols| zero-RTT]] version of the [[http://www.noiseprotocol.org/noise.html| Noise Framework]] [[https://noiseexplorer.com/patterns/NKpsk0/| IK]] pattern. A receiver shares its public key out of band in the BIP 21 URI. Static keys shared in URIs must only [be valid? be used?] for a single session. The key is encoded in [[https://datatracker.ietf.org/doc/html/rfc4648#section-5| base64url]] encoding as a payjoin directory subdirectory in the <code>pj=</code> parameter.
  • "Static keys shared in URIs must only for a single session" -> missing verb

  • (no strong opinion about "BIP21", but you wrote "BIP 21" with a space throughout this draft)


===Secp256k1 Hybrid Public Key Encryption===

Hybrid Public Key Encryption is a modern web standard for secure message exchange without TLS. Since client and server agree on a configuration out of band, we can pre-define the payjoin v2 application specific configuration to use DHKEM(Secp256k1, HKDF-SHA256) and ChaCha20Poly1305 AEAD.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Hybrid Public Key Encryption is a modern web standard for secure message exchange without TLS. Since client and server agree on a configuration out of band, we can pre-define the payjoin v2 application specific configuration to use DHKEM(Secp256k1, HKDF-SHA256) and ChaCha20Poly1305 AEAD.
Hybrid Public Key Encryption (HPKE) is a modern web standard for secure message exchange without TLS. Since client and server agree on a configuration out of band, we can pre-define the payjoin v2 application specific configuration to use DHKEM(Secp256k1, HKDF-SHA256) and ChaCha20Poly1305 AEAD.


==Backwards compatibility==

The receivers advertise payjoin capabilities through [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP21's URI Scheme]].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The receivers advertise payjoin capabilities through [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP21's URI Scheme]].
The receivers advertise payjoin capabilities through [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21's URI Scheme]].


The receivers advertise payjoin capabilities through [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP21's URI Scheme]].

Senders not supporting payjoin will just ignore the <code>pj=</code> parameter and proceed to typical address-based transaction flows. <code>req-pj=</code> may be used to compel payjoin.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Senders not supporting payjoin will just ignore the <code>pj=</code> parameter and proceed to typical address-based transaction flows. <code>req-pj=</code> may be used to compel payjoin.
Senders not supporting payjoin will just ignore the <code>pj=</code> parameter and proceed to typical address-based transaction flows. A <code>req-pj=</code> parameter, as specified in BIP 21, may be advertised to compel payjoin.

What must occur if req-pj is advertised by the receiver, but the sender doesn't support payjoin? Perhaps confirm here if it "MUST consider the entire URI invalid" per BIP 21, if that is the case.


Senders not supporting payjoin will just ignore the <code>pj=</code> parameter and proceed to typical address-based transaction flows. <code>req-pj=</code> may be used to compel payjoin.

Receivers may choose to support version 1 payloads. Version 2 payjoin URIs should enable <code>pjos=0</code> so that these v1 senders disable output substitution since the v1 messages are neither encrypted nor authenticated, putting them at risk for man-in-the-middle attacks otherwise. The directory protocol should carry on as normal, responding to payjoin requests instead with this version 1 request as BHTTP in an OHTTP response. The receiver should POST version 1 Payjoin PSBTs to the same subdirectory as in version 2 to respond to these version 1 senders within 30 seconds to respond to the sender's request.
Copy link
Member

@jonatack jonatack Jun 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Receivers may choose to support version 1 payloads. Version 2 payjoin URIs should enable <code>pjos=0</code> so that these v1 senders disable output substitution since the v1 messages are neither encrypted nor authenticated, putting them at risk for man-in-the-middle attacks otherwise. The directory protocol should carry on as normal, responding to payjoin requests instead with this version 1 request as BHTTP in an OHTTP response. The receiver should POST version 1 Payjoin PSBTs to the same subdirectory as in version 2 to respond to these version 1 senders within 30 seconds to respond to the sender's request.
Receivers may choose to support version 1 payloads. Version 2 payjoin URIs should enable <code>pjos=0</code> so that these v1 senders disable output substitution (since the v1 messages are neither encrypted nor authenticated, putting them at risk for man-in-the-middle attacks, otherwise). The directory protocol should carry on as normal, responding to payjoin requests instead with this version 1 request as BHTTP in an OHTTP response. The receiver should POST version 1 Payjoin PSBTs to the same subdirectory as in version 2 to respond to these version 1 senders within 30 seconds.
  • break up the long run-on sentence
  • "to respond to the sender's request" at the end seems a little redundant.


==Reference implementation==

An production reference implementation client can be found at https://crates.io/crates/payjoin-cli. Source code for the clients, the payjoin directory, and development kit may be found here: https://github.com/payjoin/rust-payjoin. Source code for an Oblivous HTTP relay implementation may be found here https://github.com/payjoin/ohttp-relay. The reference implementation implements an asynchronous payment flow using HTTP using PSBTv1 with encryption and Oblivious HTTP and may be configured to the following independent production relays:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
An production reference implementation client can be found at https://crates.io/crates/payjoin-cli. Source code for the clients, the payjoin directory, and development kit may be found here: https://github.com/payjoin/rust-payjoin. Source code for an Oblivous HTTP relay implementation may be found here https://github.com/payjoin/ohttp-relay. The reference implementation implements an asynchronous payment flow using HTTP using PSBTv1 with encryption and Oblivious HTTP and may be configured to the following independent production relays:
A production reference implementation client can be found at [https://crates.io/crates/payjoin-cli](https://crates.io/crates/payjoin-cli). Source code for the clients, the payjoin directory, and the development kit may be found here: [https://github.com/payjoin/rust-payjoin](https://github.com/payjoin/rust-payjoin). Source code for an Oblivous HTTP relay implementation may be found here [https://github.com/payjoin/ohttp-relay](https://github.com/payjoin/ohttp-relay). The reference implementation implements an asynchronous payment flow using HTTP and PSBTv1 with encryption and Oblivious HTTP and may be configured to the following independent production relays:

There may be a more idiomatic way to do it, but it seems best to remove the trailing . from each link so that they function.

Idem for the two links afterward and the other links in this BIP that otherwise would contain a trailing period.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, I thought they were working in the previewer. I think the idiomatic way to do it is to surround links with double square brackets [[ ]] so I'll do that.


==Acknowledgements==

Thank you to OpenSats for funding this pursuit, to Human Rights Foundation for putting a bounty on it and funding invaluable BOB Space support, who I owe a thank you to as well. Thank you to Ethan Heilman, Nicolas Dorier, Kukks, nopara73, Kristaps Kaupe, Kixunil, /dev/fd0/, Craig Raw, Mike Schmidt, Murch, Dávid Molnár, Lucas Ontiviero, Waxwing, Christopher Allen, Symphonic, Steve Meyers, Sjors Provost, Ava Chow, jbesraa, and countless plebs for feedback that has turned this idea from concept into draft, to Mike Jarmuz for suggesting that I write a BIP, and to Satsie for writing the "All About BIPS" zine which I've referenced a number of times in the drafting process. Thanks to Armin Sabouri, Ron Stoner, and Johns Beharry for hacking on the first iOS Payjoin receiver and uncovering the problem that this solves in the first place.
Copy link
Member

@jonatack jonatack Jun 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Thank you to OpenSats for funding this pursuit, to Human Rights Foundation for putting a bounty on it and funding invaluable BOB Space support, who I owe a thank you to as well. Thank you to Ethan Heilman, Nicolas Dorier, Kukks, nopara73, Kristaps Kaupe, Kixunil, /dev/fd0/, Craig Raw, Mike Schmidt, Murch, Dávid Molnár, Lucas Ontiviero, Waxwing, Christopher Allen, Symphonic, Steve Meyers, Sjors Provost, Ava Chow, jbesraa, and countless plebs for feedback that has turned this idea from concept into draft, to Mike Jarmuz for suggesting that I write a BIP, and to Satsie for writing the "All About BIPS" zine which I've referenced a number of times in the drafting process. Thanks to Armin Sabouri, Ron Stoner, and Johns Beharry for hacking on the first iOS Payjoin receiver and uncovering the problem that this solves in the first place.
Thank you to OpenSats for funding this pursuit, to the Human Rights Foundation for putting a bounty on it and funding invaluable BOB Space support, who I owe a thank you to as well. Thank you to Ethan Heilman, Nicolas Dorier, Kukks, nopara73, Kristaps Kaupe, Kixunil, /dev/fd0/, Craig Raw, Mike Schmidt, Murch, Dávid Molnár, Lucas Ontiviero, Waxwing, Christopher Allen, Symphonic, Steve Meyers, Sjors Provost, Ava Chow, jbesraa, and countless plebs for feedback that has turned this idea from concept into draft, to Mike Jarmuz for suggesting that I write a BIP, and to Satsie (Stacy Waleyko) for writing the "All About BIPS" zine which I've referenced a number of times in the drafting process. Thanks to Armin Sabouri, Ron Stoner, and Johns Beharry for hacking on the first iOS Payjoin receiver and uncovering the problem that this solves in the first place.

I don't know if people like Murch, Waxwing, and Stacie would prefer to have their real names written in addition to their nickname, e.g. Mark "Murch" Erhardt, Waxwing (Adam Gibson), Satsie (Stacie Waleyko), etc.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just Murch is fine with me.

Copy link
Contributor

@satsie satsie Jul 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: after reading the rest of the paragraph, I like the consistency and am fine staying as "Satsie" :)


The payjoin protocols automate cooperative transaction construction to break that common-input assumption. The increased opportunity to batch payments and execute transaction cut-through increases intent throughput, since multiple intents combined take up fewer bytes than independent transactions.

Payjoin V1's requirements have proven to be an obstacle to adoption. V1 coordinates payjoins over a public server endpoint secured by either TLS or Tor hidden service hosted by the receiver. Version 1 is also synchronous, requiring both sender and receiver to be online simultaneously to payjoin. Both requirements present significant barriers for all but sophisticated server operators or those wallets with complex Tor integration. Wallet developers [[https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-January/018358.html| regard]] these as limits to payjoin adoption.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: there is a mix of "V1" and "version 1" phrasing used, specifically in this paragraph, but I have seen it in other parts of the doc. "version 1" seems to be favored. This also applies to a few instances of "V2"/"version 2".


The receiver first generates a 256-bit keypair. This key will be the basis of end-to-end authenticated encryption and identification of a particular payjoin over the directory.

Rather than hosting a public server, the receiver starts a session to receive messages and allocates a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key as identifying subdirectory. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Rather than hosting a public server, the receiver starts a session to receive messages and allocates a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key as identifying subdirectory. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].
Rather than hosting a public server, the receiver starts an HTTP client session with the directory server to receive messages and allocates a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key as identifying subdirectory. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].

It might be helpful to specify a bit more about the session, since that can be a pretty general term. I'm not sure if my suggestion here is the best, but you get the idea.


The receiver first generates a 256-bit keypair. This key will be the basis of end-to-end authenticated encryption and identification of a particular payjoin over the directory.

Rather than hosting a public server, the receiver starts a session to receive messages and allocates a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key as identifying subdirectory. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Rather than hosting a public server, the receiver starts a session to receive messages and allocates a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key as identifying subdirectory. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].
Rather than hosting a public server, the receiver starts a session to receive messages and allocate a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key as identifying subdirectory. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].

This suggestion is kind of related to my previous comment. I read it as you start a session because you plan to receive messages, as well as allocate your subdirectory. However, when I initially read this sentence I questioned if the subdirectory was something local because I thought allocating the subdirectory was a separate, unrelated action from receiving messages. Since the session is to the directory server (specifying that is what my previous comment suggests), changing "allocates" to "allocate" (so that it matches the tense of "receive") allows the sentence to imply that the subdirectory is indeed part of the directory server.


The receiver first generates a 256-bit keypair. This key will be the basis of end-to-end authenticated encryption and identification of a particular payjoin over the directory.

Rather than hosting a public server, the receiver starts a session to receive messages and allocates a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key as identifying subdirectory. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part reads a little strange. Would either of these suggestions be an improvement?

Suggested change
Rather than hosting a public server, the receiver starts a session to receive messages and allocates a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key as identifying subdirectory. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].
Rather than hosting a public server, the receiver starts a session to receive messages and allocates a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key as the subdirectory identifier. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].
Suggested change
Rather than hosting a public server, the receiver starts a session to receive messages and allocates a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key as identifying subdirectory. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].
Rather than hosting a public server, the receiver starts a session to receive messages and allocates a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key identifying the subdirectory. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].

| subdirectory) | | |
+---------------------------------------------------->| |
| | | |
| Request Original PSBT | | |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to make sure I'm reading this correctly, this is when the requester starts polling for the Original PSBT, right? Before I saw the key down below for the line types, I got confused that the sender had not yet posted the original PSBT

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes indeed. If you think there might be a better way to express such a diagram I am open to suggestions.

I can put the key on the top to help.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok cool. No I don't have any suggestions 😆. If this were something richer than an ASCII diagram I'd suggest making the two types of lines more obviously different but I'm not sure if there's much you can do about it in this format.

I think it's normal/fine to leave the key at the bottom.


===Message Padding===

All cyphertexts should be padded to the same length of 7168 bytes to prevent traffic analysis. This is sufficient size for most transaction PSBTs without exceeding the 8KB limit of many HTTP/1.1 web servers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "transaction PSBTs" redundant? Or does "transaction" not refer to a Bitcoin transaction?


===Message Padding===

All cyphertexts should be padded to the same length of 7168 bytes to prevent traffic analysis. This is sufficient size for most transaction PSBTs without exceeding the 8KB limit of many HTTP/1.1 web servers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you able to provide a link to where the 7168 number comes from?


The receiver first generates a 256-bit keypair. This key will be the basis of end-to-end authenticated encryption and identification of a particular payjoin over the directory.

Rather than hosting a public server, the receiver starts a session to receive messages and allocates a subdirectory from which to relay messages. The first message must include the receiver's public key to be enrolled as a subdirectory identifier. The response message from the directory to the receiver includes the newly enrolled subdirectory payjoin endpoint with the public key as identifying subdirectory. After enrollment, the receiver awaits a payjoin request on a session identified by the subdirectory. Out of band, the receiver shares a [[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki| BIP 21]] payjoin URI including the directory endpoint in the <code>pj=</code> query parameter and a new Oblivious HTTP <code>ohttp=</code> parameter including the payjoin directory's [[https://www.ietf.org/rfc/rfc9458.html#name-key-configuration| OHTTP Key Configuration]].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the full BIP I've come to understand that the public key is specific to a session. It might be worth mentioning this up here at the top of the doc so that later when "public key" and "session public key" are used, there isn't a question on if these are separate things.


Since directories store arbitrary encrypted payloads they are vulnerable to the tragedy of the commons and denial of service attacks. Directory operators may impose an authentication requirement before they allocate a subdirectory to receivers to mitigate such attacks.

Since we make use of 0-RTT HPKE, the first message containing the sender's original PSBT has minimal forward secrecy. If the receiver's key is compromised, this message containing the Original PSBT could be read by the compromiser.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Since we make use of 0-RTT HPKE, the first message containing the sender's original PSBT has minimal forward secrecy. If the receiver's key is compromised, this message containing the Original PSBT could be read by the compromiser.
Since we make use of 0-RTT HPKE, the first message containing the sender's Original PSBT has minimal forward secrecy. If the receiver's key is compromised, this message containing the Original PSBT could be read by the compromiser.


===Network privacy===

Oblivious HTTP must be used to protect the IP address of both sender and receiver from the directory. This requires an additional key configuration to be shared in the bip21 URI and for the directory to support Oblivious HTTP. A secp256k1 HPKE OHTTP configuration should be used to leverage the cryptography already available in bitcoin contexts.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Oblivious HTTP must be used to protect the IP address of both sender and receiver from the directory. This requires an additional key configuration to be shared in the bip21 URI and for the directory to support Oblivious HTTP. A secp256k1 HPKE OHTTP configuration should be used to leverage the cryptography already available in bitcoin contexts.
Oblivious HTTP must be used to protect the IP addresses of both sender and receiver from the directory. This requires an additional key configuration to be shared in the bip21 URI and for the directory to support Oblivious HTTP. A secp256k1 HPKE OHTTP configuration should be used to leverage the cryptography already available in bitcoin contexts.

I'm only 80% sure of this grammar correction 😆

@satsie
Copy link
Contributor

satsie commented Jul 4, 2024

I really enjoyed reading this :) Well done! Dropped some comments on mainly nit related suggestions, but there were a few places where I left comments seeking clarification.

@murchandamus murchandamus added the PR Author action required Needs updates, has unaddressed review comments, or is otherwise waiting for PR author label Jul 8, 2024
@DanGould DanGould changed the title BIP 77: Payjoin Version 2: Serverless Payjoin BIP 77: Payjoin Version 2 — Async Payjoin Jul 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
New BIP PR Author action required Needs updates, has unaddressed review comments, or is otherwise waiting for PR author
Projects
None yet
8 participants