mutiny-node icon indicating copy to clipboard operation
mutiny-node copied to clipboard

Receive payjoin

Open DanGould opened this issue 1 year ago • 10 comments

Request + receive payjoin v2. This change includes v1 backwards-compatible payjoin v2 sending.

In order to become a production feature some implementation details should be considered:

Fetch Oblivious HTTP Configuration

The payjoin directory server's OHTTP Key Configuration defines it's encryption keys independent of TLS. This information is fetched in this PR using an HTTPS-in-WebSocket tunnel so it can be retrieved without revealing the client IP to the payjoin directory server in order to preserve the privacy described in BIP 77.

Display &ohttp= gateway configuration in bip21 uri

This is done via https://github.com/MutinyWallet/mutiny-web/pull/947

Oblivious HTTP Relays

Independent oblivious HTTP relays are being run by obscuravpn.io and bobspaces.net. Their URLs are included in the changed files.

closes #194

DanGould avatar Nov 01 '23 18:11 DanGould

I'm curious what ohttp adoption is looking like

Oblivious HTTP is still a draft, but is implemented in Chrome, Firefox, Cloudflare and Apple operating systems. It is used for Oblivious DNS over HTTP with a relay deployed by Fastly.

and what information is gained by running one? I guess the assumption would be that the ohttp proxy and the service are not the same people and not colluding? What, if any, information gained by the payjoin server or ohttp proxy? Trying to think through what it makes sense to host or have others host that we link to in app.

OHTTP relies on 2 distinct servers. The OHTTP Relay Resource sees the client IP and the OHTTP Gateway Resource target IP, behind which the target resource may be accessed. The OHTTP Relay sees an encrypted ohttp-req message. It does NOT see the contents of the message. It only sees an ohttp-res message and not the contents of a response either.

The OHTTP Gateway sees that the request came from the OHTTP Relay IP, and may decrypt the contents of the request: a second layer of e2ee between sender and receiver and the pubkey associated with that session (a pseudonymous subdirectory identifier for the session)

The threat model in the BIP assumes they do not collude. If they do collude, they may correlate the subdirectory with sender and receiver IP addresses to discover only that 2 IP addresses may have communicated with one another at the Payjoin relay subdirectory at a given time.

image

Would users be able to select their own ohttp proxy and their own payjoin servers? Ideally we don't have to host or hard code it in order for it to work, though potentially willing to if it makes sense.

I would suggest users don't select their own proxy for the initial rollout for three reasons.

  1. Dependency compatibility: OHTTP's specified Hybrid Pub Key Encryption HMAC-based Extract-and-Expand Key Derivation Function does not support the secp256k1 curve. I've specified a change which uses secp256k1 so that bitcoin software does not need any external crypto in order to use payjoin v2. Secp256k1, sha256, and chacha20poly1305 are all already part of the bitcoin stack and secure for the purposes of such a protocol. There is a formal process to get secp256k1 into the spec Key Encapsulation Mechanisms via IANA and potentially the browsers/operating systems. However, the spec does not prevent us from building working software today. EDIT: someone is already registering secp256k1 with IANA!
  2. UX: Any external relay would need to support secp256k1 or potentially be incompatible with a payjoin peer. Any draft spec external OHTTP Relay will not support secp256k1 at time of writing. If a payjoin Relay relied on an OHTTP Gateway configuration using only curves other than secp256k1, which is guaranteed to be available in bitcoin software, some payjoin-enabled wallets may not be able to communicate with them.
  3. Privacy: Mutiny clients already reveal their IP address to Mutiny and don't need to reveal it to another service. There is also an anonymity set benefit of having as many users using the same OHTTP relay as possible.

DanGould avatar Nov 02 '23 18:11 DanGould

By running an OHTTP relay, could anyone use that to connect to any other OHTTP gateway? I'm guessing the relay would see what IP is connecting to what gateway, but could there be multiple services behind any given gateway?

TonyGiorgio avatar Nov 03 '23 05:11 TonyGiorgio

By running an OHTTP relay, could anyone use that to connect to any other OHTTP gateway?

In section 8.2: "The relay will only forward for Oblivious Gateway Resources that it has explicitly configured and allowed."

I'm guessing the relay would see what IP is connecting to what gateway, but could there be multiple services behind any given gateway?

Yes, yes, and specific path, query, methods, request body, headers are all encrypted from the ohttp relay's view

DanGould avatar Nov 03 '23 13:11 DanGould

Okay thank you, this is very helpful. Just trying to understand the operational side of OHTTP and whether we should stand one up. Regardless of who runs what, I like the approach and the code is clean and makes sense. Appreciate the work there!

TonyGiorgio avatar Nov 03 '23 16:11 TonyGiorgio

edit: ohttp-relay is now available as a docker container here: BYO ssl cert and the rest just workstm

FYI standing up a relay is as simple as adding a couple blocks to nginx if you're already running it somewhere

http {
    # HTTP Server Block
    server {
        listen 80;
        server_name your-server-name.com;

        location /ohttp-relay {
            # Forward requests to the OHTTP Gateway
            proxy_pass https://ohttp-gateway.example.com;

            # Clear the X-Real-IP header
            proxy_set_header X-Real-IP "";
            # Make default proxy_pass behavior explicit for good measure

            access_log /path/to/http-access.log;
            error_log /path/to/http-error.log;
        }
    }

    # HTTPS Server Block
    server {
        listen 443 ssl;
        server_name your-server-name.com;

        ssl_certificate /path/to/your/certificate.crt;
        ssl_certificate_key /path/to/your/private.key;

        location /ohttp-relay {
            # Forward requests to the OHTTP Gateway
            proxy_pass https://ohttp-gateway.example.com;

            # Clear the X-Real-IP header
            proxy_set_header X-Real-IP "";
            # Make default proxy_pass behavior explicit for good measure

            access_log /path/to/https-access.log;
            error_log /path/to/https-error.log;
        }
    }
}

DanGould avatar Nov 10 '23 19:11 DanGould

This should now be receiving v2 payjoins, however the persistence and asynchronous completion is still a bit janky in this form factor. It does not seem like resume_payjoins is properly resuming sessions on startup.

We've also got to consider how to display pending payjoin in activity. It seems like that UI list is only considering broadcasted transactions and pending lightning payments at the moment. This introduces a new tx pending state where a PSBT has been signed and posted to a payjoin directory but not yet responded to (from a sender) or responded to but not yet broadcast by the sender (for a receiver).

I think v2 payjoin receivers who sign should show transactions pending, since only 1 step remains before the transaction is broadcast, and they've effectively been probed and revealed their utxo. v2 Senders perhaps should not treat a single signature as pending, since they both need to have receiver sign and check the result, at least until BDK allows us to lock UTXOs. Still, a sender in this state could have their original psbt broadcast by a receiver. I'll have to look into how you're handling pending lightning payments to become inspired with a solution regarding pending payjoins.

DanGould avatar Dec 22 '23 17:12 DanGould

I think it's valid to consider it as pending in the UI. I think we just do a filter on ActivityItems before returning it. We may have to save these in our DB for it to reflect state accurately.

TonyGiorgio avatar Dec 22 '23 17:12 TonyGiorgio

This is now based on the payjoin-0.13.0 release, persists receive payjoin sessions, and adds pending transactions to the wallet using insert_tx and cancel_tx before they hit mempool. Though tracked payjoins are not explicitly marked as such. Their PSBTs are just put in the ActivityItem::Onchain list. One way to explicitly mark them as payjoin would be to manually construct payjoin TransactionDetails, add an is_payjoin field or similar, store them in MutinyStorage, and pull from that list in get_activity() if this solution seems incomplete. This solution should work with the existing add_onchain_labels since it attaches labels to addresses.

For now, the way the receiver here gets payjoin directory's ohttp-keys does reveal IP address to the directory but that will be solved with the next deployment of ohttp-relay @teemie1

This would require the addition of &ohttp= bip21 param parsing, ideally in bitcoin-waila, for ui support.

It might make some sense to separate backwards compatible send payjoin support in another PR before this one to simplify review. #940 makes that possible.

DanGould avatar Jan 06 '24 19:01 DanGould

While I've left a few TODOs that are in progress, this change is ready for deeper review

DanGould avatar Jan 15 '24 18:01 DanGould

Sorry for the delay on this.

I want to be transparent, that we're pretty unsure about what we want to do with the on chain wallet long term. We're really not optimizing for on chain related activities and it has just caused various friction. Ideally long term we get to move towards something like splicing and can do splice outs for on chain payments, but until then, we've talked about removing the on chain wallet and defaulting to on chain going into a fedimint.

I've seen the discussions here which are great to see but would imagine it might be complex.

Not sure about this TBH, but curious your thoughts here.

TonyGiorgio avatar Mar 21 '24 18:03 TonyGiorgio