Socratic Seminar Payjoins (2020-05-05)
Transcript By: Michael Folkson
Name: Socratic Seminar with Adam Gibson
Topic: Bitcoin privacy (Payjoin/P2EP)
Location: London BitDevs (online)
Date: May 5th 2020
Adam Gibson Pastebin on Payjoin resources: https://pastebin.com/rgWVuNrC
6102bitcoin resources on Payjoin/P2EP: https://github.com/6102bitcoin/CoinJoin-Research/blob/master/CoinJoin_Research/CoinJoin_Theory/P2EP/summary.md
Transaction fingerprinting discussion: https://github.com/zkSNACKs/WalletWasabi/issues/3625
BitDevs resources on Coinjoin: https://bitdevs.org/2020-02-19-whitepaper-series-11-coinjoin
The conversation has been anonymized to protect the identities of the participants.
Intro (Michael Folkson)
We are live, London BitDevs Socratic Seminar on Bitcoin privacy with Adam Gibson. This is a live call on Jitsi. We are streaming on YouTube so bear that in mind. Thanks to Jeff from Fulmo for helping us set this up. For those who don’t know Jitsi is open source, it doesn’t collect your data and it was surprisingly easy to get this set up. Let’s hope there are no problems today and we can show how wonderful Jitsi is. So far so good. This is London BitDevs. You can follow us on Twitter @LDNBitcoinDevs. We have a YouTube channel with some really good presentations from the last couple of years. Adam Gibson has talked about Schnorr, Chris Belcher has a couple of presentations on Joinmarket and Sjors Provoost and John Newbery and all sorts. We are also looking for future speakers. If you are a Bitcoin developer or a Lightning developer and you are interested in speaking about work you have been doing or a particular topic get in touch. We can do a presentation or we can a Socratic Seminar like today. I’ll talk a bit about Socratic Seminars. It originated at BitDevs in New York. It is a discussion normally with an agenda that the organizers put together and you discuss all the various different resources on that agenda. The emphasis is on participation and interaction. This isn’t supposed to be a presentation from Adam today, this is supposed to be a discussion. We will try to get as many people who are on the call involved in that discussion as possible. For resources on doing Socratic Seminars online or in person check out bitdevs.org. Introductions, Adam Gibson I think everyone will know. He works on Joinmarket and a bunch of Bitcoin privacy projects. We have got a number of people on the call including Max Hillebrand and openoms. We normally start the Socratic by doing introductions. Obviously you don’t have to give your real name if you don’t want. Also if you don’t want to do an introduction that is also fine.
(The participants in the call then introduced themselves)
Bitcoin privacy basics
For the benefit of the beginners who are perhaps watching on YouTube or maybe some of the beginners/intermediates on the call we will start discussing the basic concepts first. It will get advanced technically later on. Obviously we have got a bunch of developers who are working on privacy tech on the call. What are the problems with Bitcoin privacy? What are we worried about when we make a Bitcoin transaction in terms of privacy leakage?
Satoshi wrote in the white paper that the privacy is based on pseudonymous identities, Bitcoin addresses in the very general sense. What we want to achieve is to delink these pseudonymous identities so that one user having several of these is not tied to or revealed in the cluster. The easiest way to get deanonymized is by reusing addresses. Then it is clear that this is the same pseudonymous identity. Even if you don’t reuse addresses there are certain ways that you can transact Bitcoin that still reveal ownership of coins and addresses for a specific user. These are heuristics. There is the common input ownership heuristic. Basically all these deanonymization techniques rely on the fact that they can link different pseudonymous identities to the same cluster.
I would expand a bit further. This is not just at the level of the transaction structure. All transactions are completely transparent because that is how everybody can validate. Also all of the other underlying infrastructure like the network level privacy is full of concerns. There is no encryption in the peer-to-peer protocol. There are many facets to this, not just the transaction structure.
There are a couple different aspects. There are things recorded on the blockchain and preserved forever and can be analyzed. There is also metadata that can be related to the network, IP addresses recorded together with the transactions in some databases that are outside of the blockchain. There could be timing analysis, trying to locate the source of the transactions. Also there are databases which are different from the blockchain but listing the transactions. The big exchanges or anything running with KYC which are connecting the transactions and withdrawals with the passport and personal data of the users. There are other things that we need to be aware of.
What is a coinjoin? What is the problem that a coinjoin is attempting to solve?
Coinjoins is joining multiple transactions. Multiple inputs creating a certain number of outputs.
I think the main problem is the connection between outputs. I mostly make the analogy to a twenty dollar cash bill. If you want to buy something for five dollars and you give the person a twenty dollar cash bill you get fifteen back. It is mostly known that the fifteen dollar are from the sender of the first payment. And so you can correlate the amount spent and the amount received back. This is the most common transaction type where you pay with one output or several outputs to one address and create two new outputs. One for the receiver and one as a change output.
The idea with coinjoin is to literally join your coins. This is a way for multiple users to consolidate several of their coins in one transaction. The important idea is to break the common input ownership heuristic. That is one part of it. We can no longer assume that all inputs of a transaction belong to one entity. As would be the case for transaction batching where one entity does several payments in one transaction. It is a scalability improvement. A further privacy result that we want to get from this tool is that the outputs being created are no longer tied to the inputs. The pseudonymous identities of the input user are no longer linked to the output users.
One last thing is that coinjoin is completely trustless. It relies on the fact that every input requires a signature that authorizes the entire transaction. So unless all the participants agree about where the funds end up there is no possibility of theft.
Another thing that we are exposing in transactions is not only that the actual UTXO can be tied to an entity but also the payment history and the future payments from that point when the inputs are tied to someone. By employing a coinjoin what we can do is break the UTXO from its history. If we pay through a coinjoin or pay after a coinjoin we can decouple from the future history of the coins.
What are the challenges when setting up a coinjoin?
There is fundamentally a coordination problem between the different participants. If you want to participate in a coinjoin you have to find other Bitcoin users who are willing to collaborate with you to construct this transaction. There are privacy considerations in whatever protocol you use. When you communicate with those parties to this transaction you generally will disclose more information if you do it naively. It is both a coordination problem, finding counterparts, and it requires additional layers of privacy to actually go through that whole process in a way that maintains the benefits of a coinjoin from the blockchain layer perspective.
We already spoke about the non-custodial aspect that nobody can steal and the privacy aspect that none of the participants can spy on each other. I think one important nuance that is very difficult to do in the implementation is denial of service protection especially because we work with pseudonymous identities here. It might be very easy for an attacker to do a sybil attack or to break the protocol at a specific time to simply delay it or make the service unusable. Denial of service is a very nuanced thing that we might not even have seen working properly in production yet.
We can speak about how to construct this transaction. These participants come together but then there must be a coordinator who puts together all the inputs and all the signatures and all the outputs. That can be a central entity like in the case of Wasabi or Whirlpool or can be decentralized in terms of one entity who is acting as the taker in the Joinmarket model.
So let’s move on to the subject of today, payjoin. What is a payjoin?
If I try to stick with the cash example. The same as before, I want to buy something for five dollars. I give the seller twenty dollars, the seller will give me five dollars and I give him twenty and five dollars. He gives me some cash back. On the perspective of the blockchain outsiders can’t decide which input or which cash comes from me and which comes from the seller. The receiver of the payment gives some money to the buyer and receives mostly more back. It is to decorrelate.
I think that is mostly correct. It is better to think of it as last steps. Before the buyer would pay the seller would offer him some extra money to be included in that payment. Let’s say they put it on the table, the seller puts the product they want to sell on the table and they also put another twenty dollars on the paper. The buyer will pay the price of the product plus twenty dollars.
What do we want to try to break on the heuristic side? Firstly of course the common input ownership heuristic again because now not only the sender provides an input but also the receiver provides an input. That is very nice. The cool thing is that this heuristic is now weakened not just for the payjoin transactions but for every coin consolidation transaction. Every transaction with more than one input might be a payjoin and thus the common input ownership heuristic does not hold up as often as it used to. That is a huge win already. Another point is the actual economic value transacted. If we take the previous example. Alice wants to pay for the pizza with 1 Bitcoin and Bob has 0.5 Bitcoin that he adds to this transaction. All of a sudden Alice gets her change back, for example 2 Bitcoin. But the output that belongs to Bob not only has the 1 Bitcoin transaction for the pizza but also the 0.5 Bitcoin additionally from Bob. This output is 1.5. Although the economic value transacted was actually 1 Bitcoin. This is obfuscated as well.
It is interesting when you try to make the analogies with cash work. It is a good example where it doesn’t work too well I think because of the nature of cash. It is more fungible in its nature than Bitcoin with these txo objects that have a name on them. Let’s not get bogged down in analogies that don’t work too well. We want a few things. We want coinjoins that have the effect of breaking the common input ownership heuristic and we want them to be steganographic. Steganographic means that you are hiding in the crowd. You are doing it in a way where your transaction doesn’t look different at all from ordinary payment transactions. You are getting the coinjoin effect but you are not making it obvious that you are creating the coinjoin effect that creates a very strong additional privacy boost to the whole network because it makes blockchain analysis less effective. There has been some discussion recently about the purpose of payjoin. I think when I wrote a blog post about it the last sentence I wrote was something like this is the final nail in the coffin of blockchain analysis. That is the dream of it I think, that it makes blockchain analysis horribly ineffective. Currently we have a situation with coinjoins with equal sized outputs that have a tremendous benefit in delinking those outputs from the inputs. They have a significant benefit but the problem is currently as we understand it, this is a hot topic that nobody is 100 percent sure about it, blockchain analysis firms are marking those transactions as mixes and putting them to one side in their analysis or they are doing some crude percent based taint analysis where they say these coins are 20 percent connected. In either case they can see on the blockchain that these coinjoins are happening. What we are trying to do with payjoin is to trade-off having smaller anonymity sets per transaction because we are talking about two party coinjoins, that is intrinsic to the fact that a transaction is two party. A transaction doesn’t have to be two party, I could pay three people. We could get more complicated with this. For now we are just sticking with the two party case. It is intrinsically a small coinjoin with a couple of people in it. It is clearly not as powerful as using exactly the same output size which is intrinsically indistinguishable. This is not an intrinsically indistinguishable situation. If somebody looks at a payjoin they might be able to figure something out. We will get into that later. That is giving you the thousand foot view of where the motivation is. What I want to do for the next twenty minutes I want to keep this fairly brief, is to go through the history of payjoin as an idea.
History of Payjoin (Adam Gibson)
This pastebin was me writing in sequence all the things I remember about payjoin. I’ve got all those links open in my browser. Earlier descriptions, before anyone used the word payjoin, obviously coinjoin as a concept was first expounded in 2013 by Greg Maxwell. As should always be noted coinjoin is not an innovation in Bitcoin at 2013, it always existed from the start as something in principle people could do. He mentioned it before 2013, I think he called it I taint rich, this is way back in the annals of Bitcoin history. Coinjoin itself, it is there from the beginning, payjoin is a variant of that central idea that when you create a transaction it doesn’t have to be just your inputs. The strong power behind that idea is this crucial issue that at least with standard sighash flags, if you sign a Bitcoin transaction with your key on one of the inputs you are signing the whole transaction therefore fixing that signing to the specific set of outputs. You don’t take any risk in signing one of the inputs to a transaction and having someone else sign another input to the transaction. You can still make sure that you get the money you expect to get. Consequently there are various different models for coinjoin. This particular one was not taken very seriously by many people including myself because we thought it was a pain for a payer to coordinate with a receiver instead of the usual model of Bitcoin which is a payer prepares a transaction on their own and doesn’t even have to talk to the receiver. One interesting detail is that since Lightning has become quite a big thing in the last couple of years, it also has an interactive model of payment. There is some new technology that gets around that but let’s just say Lightning generally involves an exchange of messages between sender and receiver. It may be that subtly that influenced the thinking of the people including myself at a meeting in London in 2018 where we discussed possible ways to make coinjoin more widely used. The first link here is one of the first things that came out of that meeting in London. At the time Blockstream put out an article, it was Matthew Haywood who was at the meeting talking about pay-to-endpoint. At the time I thought this was a poor name. I thought payjoin would be a better name. That debate still exists. It is not a very interesting debate about names, who cares? If you see pay-to-endpoint (P2EP) and you see payjoin think of it as basically the same thing. Let’s say it is the same thing because it is not interesting to argue about. He wrote a long blog post. He gives all the basic principles. Towards the end he talks about some interesting questions around protecting against potential privacy attacks. This is still an interactive protocol. Even if it is only two party there could be scenarios where the sender could be trying to steal privacy from the receiver by for example getting lots of information about their UTXOs. There is some discussion of that there. There is some high tech ideas about how to solve it. You will see the nuances as we go on. That was one of the first explanations of the concept. I think technically nopara who was also at the meeting at me put out a blog post here on Medium. He was very interested in this idea how can we prevent senders stealing privacy from receivers. We were looking at zero knowledge proofs and so on. He talks about that and as usual he has got cartoon characters. He always has some interesting pictures in his blog posts. I wrote this very dense wordy blog post. I think it is one of my better blog posts even though it is not very pretty. It does go through some nice pretty easy to understand examples that lead you into payjoin. For example, asking the question hiding in a much bigger crowd, how could we do a coinjoin but make it look like an ordinary transaction, that is the story I am trying to tell here. I draw examples like here where Alice and Bob do a coinjoin where they both put in 0.05 and they both get out 0.05. That is stupid. There is no real transaction. There is no payment transactions that look like that so that is a failure. Then I talked about how could you make a coinjoin where this looks a bit more like a normal payment because it has two outputs. Normal payments have a payment output and a change output and some inputs. It could be 1, 2, 3, 4. Who knows? The problem with this kind of thing is less obvious but it is very revealing when you understand the problem with this. 0.01 + 0.04 adds up to 0.05. 0.03 + 0.03 adds up to 0.06. That is slightly unobvious but on reflection obvious that a subset of the inputs, two of them, add up to one of the outputs and the remaining subset of the inputs adds up to the other output. We call this subset sum analysis. Crudely separating the inputs and outputs into different subsets and seeing that they match up is a dead giveaway, not 100 percent certain, that what is happening there is a mixing event where two parties are almost certainly not paying each other they are paying themselves and keeping the same amount of money they had to start with. This is a central problem if your goal is to try to create coinjoins breaking the common input ownership heuristic but at the same time you don’t want to give away that you are doing that. That is where payjoin comes in. Payjoin is accepting the inevitable. If I want a mixing transaction to look like a payment transaction it unfortunately does have to be a payment transaction. Is that 100 percent true? No. But payjoin says “Let’s accept that. Let’s do mixing transactions in payment transactions”, in other words together at the same time. That’s why we end up with stuff like this where if you can see here we have actually Alice paid Bob 0.1. If you imagine looking at this on the blockchain you certainly wouldn’t expect that there was a payment of 0.1 Bitcoin. But actually according to that logic there was. It is because Alice and Bob coordinate so that Bob, the receiver, actually contributes this input and he receives this back this output. The net difference between the input he provides and the output he receives is 0.1. So he was actually paid 0.1 even though it is not obvious.
Unnecessary input heuristic
There were a few blog posts there. I am not sure any of them are superb. What is good is a nice simple summary on the Bitcoin wiki. It goes over the basic ideas here in a couple of paragraphs. We had the idea, it was out there in 2018. It had become concrete as an idea that we would really like to do this. You will find discussions on the mailing list from late 2018. Before I started writing any code for it though I did want to figure out a couple of details. This link is interesting not so much because of the content of the description of a protocol for payjoin but because of the discussion that happened afterwards particularly when LaurentMT raised several points. This is a discussion between myself, Chris Belcher and LaurentMT. We are discussing different heuristics that might give away… Even though in the example I showed you you don’t know it is a payment of 0.1 because you can’t see the amount, there is more to it than that. How do wallets select inputs to provide the necessary amount of Bitcoin to make the payment. Every wallet is a bit different in this regard but there are various rules you can think about. What is discussed here is something we ended up calling the unnecessary input heuristic (UIH). I am not going into it now because it is going to take a bit too long but I strongly recommend reading it. We even broke down the unnecessary input heuristic into two types. To give a high level idea, if you are going to be paying 1 Bitcoin you wouldn’t use a 2 Bitcoin UTXO and a 10 Bitcoin UTXO to pay 1 Bitcoin. Because the 2 Bitcoin UTXO on its own would have been enough. Is that an absolute statement? Nowhere near is it absolutely true because wallets have really complicated ways of choosing UTXOs. That’s the general idea and there is a lot to investigate there. It is still an open question what one should do about that in the context of payjoin and it doesn’t look suspicious. That was the discussion on that gist that I linked there.
Existing implementations, here I am talking about things that were implemented in 2018, 2019. At that time I worked on a payjoin implementation within Joinmarket. That is that link there. It is just code. That was an implementation where the idea was let’s do payjoin but only between two Joinmarket wallets because it is much easier that way and where we use the existing messaging infrastructure where Joinmarket bots talk to each other end-to-end encrypted over IRC servers. Let’s use that messaging channel and coordinate that protocol so that one Joinmarket wallet owner can pay another Joinmarket wallet owner. Around the same time Samourai did what they called Stowaway. There is some high level description and some code. They were basically doing the same thing as what I did in Joinmarket which was do payjoin but they called it Stowaway, between two Samourai wallets. Two Samourai wallets can pay each other with Stowaway and use this exact technique where the receiver contributes UTXOs to the transaction thus breaking the common input ownership heuristic and making the payment amount unclear. Those were early implementations. The real issue of course is that payments are often between parties not using the same wallet. In fact the majority of payments occur between parties not using the same wallet. We really needed a standard and that started getting discussed. The first person to take the initiative on that was Ryan Havar who called his proposal Bustapay. He created BIP 79 and it lays it out in fairly simple terms. The focus here is not so much on two peers using a phone wallet or using a software wallet on their desktop but a customer paying a merchant. It doesn’t insist on that but that’s its focus because it is a client server architecture where the server is a web server. He describes in detail a protocol where using a HTTP POST request you could put in the body of a request and proposed transaction. Here is the smartness of a good payjoin protocol comes in. We discussed this at that London meeting in quite some detail. The original proposer who wants to pay with a payjoin, you’re a merchant who is selling widgets for 1 BTC. I’m a customer, I want to pay you with payjoin because it is better for privacy. I don’t just say “Please can I do a payjoin?” I send you a payment which is not a payjoin which I’ve signed because I am intending to pay you. Instead of broadcasting my payment on the network which is the normal workflow and you as the merchant notice the payment on the blockchain, I sign the transaction as normal but do not broadcast it and I send it to you in the body of a POST request. You receive that and you look at it. You say to yourself “That looks like a decent correct payment of this invoice.” Now because he has used the payjoin flag let’s say I am going to send him back a partially signed transaction and he is going to co-sign that and complete the payjoin. Remember that is the whole thing about payjoin. Like any coinjoin both parties or more than one party has to sign the inputs. He signs his input(s), sends it back to me as the response to my request. I look at what he has sent me and say “That is still basically the same transaction. I am still spending the same amount of money, I’m not getting cheated.” He has signed his inputs, his inputs look the right type like they are the same kind of input as my kind of input. There are a set of checks. Then I say “I agree.” I cosign that payjoin transaction and I do broadcast that onto the network unlike the original proposal transaction which I did not broadcast on the network. Then you as a merchant, you keep an eye on the blockchain. If you see the payjoin transaction was broadcast, great, that means your invoice was paid. If you don’t after 1 minute you have in your back pocket the original payment transaction that I sent you. The nice thing about this protocol is that it ensures that the payment goes through whatever happens. Can anybody explain why is it important that in the payjoin protocol the customer who is paying sends this non-payjoin transaction first?
It is very important to limit the queries the payer can do because otherwise if he didn’t send over a valid transaction first then he could keep asking until he knows all the UTXOs in the wallet. That would be a big privacy leak.
It is denial of service protection. To have an economic cost for users misbehaving. This is a payment coinjoin so the user actually wants to buy something from the merchant and he wants to make that transaction anyhow. He can sign it and if the receiver is no longer online or something goes wrong it is just broadcasted anyhow without the added privacy of the payjoin but it is a final transaction.
I could expand on that question because I think this is a really important point. Does anybody think that that anti snooping measure, where people find out all the contents of the merchant’s wallet as they iterate over their UTXOs. Does anyone think that this measure is not sufficient to stop that attack?
I think it is not sufficient because if the original payment proposed is higher than the amount in the seller’s wallet it could expose the whole wallet in the first step.
I’m not sure how avoidable that is. If you are a merchant and if you have widgets for 100 dollars.. it is a really tricky point.
A complication is that the merchant has to select a specific input that they would like to join. If that process is not deterministic that means multiple requests would reveal their funds. I think this is orthogonal to having the payment amount. If a user sends a payment, whatever the merchant responds with should be determined completely by what they intended to receive so that this request for Bitcoin does not leak more than the minimum information required to participate in this protocol.
I don’t know if everyone here has read BIP 79 but he was quite specific about this point that the merchant must be careful to always respond with the same proposed joining UTXO if he is receiving requests…
“To prevent an attack where a receiver is continually sent variations of the same transaction to enumerate the receivers utxo set, it is essential that the receiver always returns the same contributed inputs when it’s seen the same inputs.” (BIP 79)
I don’t think he explained it the best way there but I understood him to mean that if someone keeps sending a proposed transaction spending the same UTXOs from the sender you should return the same contributed UTXOs as the receiver. There is a slight detail that is maybe missed there. What if the sender sends a new version where the input set is not identical but is overlapping? I didn’t really want to go into in extreme detail here because I think this is a tricky point. I did want to raise because this discussion is maybe not entirely finished. When people think carefully about how you might attack payjoin there are various different angles to attack it. Certainly this thing of snooping is something that people are going to try hard to get it right. I think that principle he expresses there is a very useful one. But I’m not sure that even that is quite enough to say “This is a safe thing. There is no possibility of snooping.” Another high level consideration to bear in mind is by the nature of being merchants they are exposing quite a bit of information. Businesses generally at least in the current version of Bitcoin where the protocol is completely transparent in terms of amounts and the transaction graph, buying stuff from a merchant is a way to expose information about them. That was seen even in the very earliest academic analysis of Bitcoin privacy in 2013. If you look on the Bitcoin privacy wiki page you will see a section called the Mystery shopper payment Chris Belcher who wrote that explains in some detail there. Anyway it is a big topic but I wanted to highlight it because it is one of the more important conceptual and technical issues in the idea of payjoin.
One way to limit that interaction is by making the endpoint disposable. You create an endpoint for receiving only one proposal. One use only. That is possible to make it easier. If your endpoint is a Tor onion address you can create as many disposable addresses as you want or you can manipulate the URL in your website to make those endpoints disposable. The buyer doesn’t have a valid endpoint to continue making proposals to you.
This is where the subtlety of whether it is a peer-to-peer payment or a merchant payment comes in because if you did that as a merchant you would just be spawning out new onions or endpoints for every payment request. It doesn’t make any difference in that case because that is how it works. I was just experimenting with a BTCPay Server instance that Mr Kukks set up and I just kept pinging it with different requests. It doesn’t matter if it is an onion there does it?
It doesn’t matter really, you are right. For example, for a scriptPubKey you can associate an onion address to a scriptPubKey and make it disposable. You can only receive one proposal for that scriptPubKey.
One thing that I also just realized is that the sender can do a double spending attack because he makes the first signed transaction and then he receives the pre-signed or partially signed transaction from the receiver. So he knows the fee rate. He can simply not sign the second transaction, the payjoin transaction and double spend the original transaction paying back to himself with a slightly higher fee rate. If he is the one to broadcast transaction first the original transaction may not even be broadcast because full nodes will think it is a double spend if RBF is not activated. That might be an issue to.
I can’t remember the thinking around that. I seem to remember reading about it somewhere, that is an interesting point for sure. Let’s keep going because we want to get to the meat of it, what people are doing now. I wanted to give the history. That was the BIP (BIP 79). It is a very interesting read, it is not difficult to read. I did have some issues of it with it though and I raised them in January 2019. Ryan Havar had posted the proposal and I wrote some thoughts on it here. Fairly minor things, I didn’t like the name. Protocol versioning, other people didn’t seem to think it mattered that much though apparently some of them are changing their mind now. Then there was the whole thing about the unnecessary input heuristic which we talked about earlier. It is interesting to discuss whether something like that should be put in a BIP, a standardization document, or whether it should be left up to an implementation. We will come back to that at the end. There are some other technical questions, you can read that if you are interested in such stuff. The conversation in different venues has been ongoing.
Payjoin in BTCPay Server
The big change recently was this one from BTCPay Server. Here is now the spec document that they have written and is still undergoing changes as we speak. Payjoin-spec.md in the btcpayserver-doc repo, it describes a delta to that BIP 79 that we just looked at. It is the same basic idea in terms of making a HTTP request to some endpoint. I forgot to mention importantly BIP 21 URI. What is a BIP 21 URI?
BIP21 URI is a URI with a Bitcoin scheme. A scheme is just a standard thing for all URIs that describe the protocol. Following that is a Bitcoin address and optionally some metadata parameters. It is just a standard format that makes it so that you can click on a payment and if you have a wallet configured locally on your computer it could go from an email or a website directly to that wallet with the correct payment amount and the correct destination address. I think there is an optional payment description as well, I don’t remember the details.
I think the idea is that you can have several optional fields. You could also have required fields such that if it is not recognized then that is not allowed. An example of this is Mr Kukk’s donation BTCPay Server instance. You can see that you’ve got this scheme is Bitcoin, we have been using this since 2012, 2011, it has been around a long time. It is an early BIP. You have the address, amount and critically in this case
pj meaning payjoin, the flag that tells the merchant or whoever is receiving the payment that this is intended to follow that payjoin protocol that BTCPay Server are defining in that document which is itself a delta to BIP 79 which had Bustapay instead. This just has payjoin. It is a delta that doesn’t change that much. It changes a few things about the communication between the parties. The most important change apart from trivially the name is that it uses PSBT. This is one of the things I was complaining about in the original proposal at the time in Bustapay, I thought we’ve got to use PSBT because everyone is obviously going to migrate to that. It is a sufficiently flexible standard for cross-wallet sending transactions. Does anybody not know what PSBT is? It means partially signed Bitcoin transactions. It is a standard, BIP 174. It does actually allow you to use a raw hex format for a transaction instead of PSBT. As far as I’m aware everybody who has implemented this is using PSBT. This adds a little bit more detail. It also adds stuff that is very useful. If you ever find yourself in a position where you want to implement payjoin you’ve got the advantage that it tells you all the checks that you should be doing on both the receiver and sender side. It has some interesting considerations about the relationship between the choices we make in building these transactions and the position of the blockchain analysts. That is what we are going to move on in the next few steps. They brought out that document and they implemented it within BTCPay Server. Does anyone know when they pushed that out? Three or four weeks ago? It was recently.
In this merchant scenario could the merchant help coordinate combining purchases from different shoppers in the same transaction to mix more UTXOs?
I think that is difficult. Something simpler than that but somewhat similar, one thing that was discussed in the BTCPay Server doc was receiver’s payment batching. It is a nice thing, it is not hard. The way the protocol is defined the sender of the payment doesn’t bother looking at the outputs which do not belong to him. That allows the receiver to be flexible and batch his own payments to his own suppliers, he may want to pay three different people at the same time, he can batch that into the payjoin. This makes it more economical for him at least and also confuses the blockchain analysts a little bit more, that is debatable. The other idea of having multiple payers paying at the same time, it brings us back to that coordination problem. When you try to coinjoin with 4, 5, 10 people they have all got to be there at the same time. The good thing of course is that they don’t have the problem of coordinating the same output amount because they are all making their own payments, that is fine. The bad thing is that they all have to make their payment at the same time. Today I was testing this protocol and it was cool doing coinjoins that take less than one second. I was pressing on the keyboard and the payment just went through. I am so used to in my head dealing with Joinmarket where it takes a minute to figure everything out and send the data back and forth. The answer is yes in theory but there are some practical difficulties there.
There is a privacy concern for the payers. They might learn about each other’s inputs and then refuse to sign which I think is a serious issue.
To reiterate, there is a coordination problem, a privacy problem and a protocol jamming problem. The protocol might not finish. Whenever you coordinate between multiple parties you have to deal with this stuff, it is pretty hard. The upshot is that it would be very hard to do that.
Can I have two wallets and make a payjoin to myself just to fake it and get more anonymity?
The thing with payjoin is that it is steganographic. It is really not clear that the common input ownership heuristic is broken, it might look like it upholds. If you fake a payjoin you are just doing transaction batching or input consolidation. In your case the common input ownership heuristic. You might be deanonymized or you might not be, it may fool someone. As we try to get payjoins steganographic this would reveal the common input ownership heuristic.
I think one can answer a little simpler than that even though that is correct. By steganographic we mean that we are trying to make coinjoins that look like ordinary payment transactions. If you make a payjoin to yourself it is not achieving any goal that I can understand because you are just making a payment to yourself. Notice how it is different from doing a fake equal output coinjoin. I’m not arguing that that is a particularly good or bad thing to do but if you do that you are creating a coinjoin which blockchain analysts may choose to flag and say that is a mixing transaction. Here you are creating something that looks like an ordinary payment so they wouldn’t flag it. You may well raise the question is it interesting to do payments to yourself, multiple hops kind of thing. Maybe, maybe not. It is a complicated question. Intrinsically it is steganographic, that’s the main answer so it doesn’t really make sense.
If I would like to top up my second wallet can I make a payjoin to my second wallet to fake it and get more anonymity from it?
My answer is what is the difference between doing that and just paying your second wallet? I don’t see the difference. In terms of their onchain footprint they look the same. An ordinary payment and a payjoin, that is the idea.
Does payjoin usually fragment or aggregate UTXOs? I assume for privacy it would fragment. In that case do you think it could cause a long term effect on the size of the UTXO set on nodes?
I am going to give my approximate answer, I’m sure others have opinions on this. As far as I understand unlike equal sized output coinjoins which do create extra UTXOs…. Equal sized coinjoins over time are not going to make your wallet worse than some kind of equilibrium state. But they are obviously using up a lot of blockchain space. Payjoins, I’d argue maybe even better because you are making a payment anyway so the only thing that is happening is on the receiver side. On the sender side it doesn’t change anything. You use up the same inputs you would have used anyway, you get back the same change you would have got back anyway, nothing has changed there. For the receiver though it does change the dynamics of their wallet because if you were receiving let’s say 10 payments a day, a merchant receiving Bitcoin, if you don’t use any payjoin at all you are going to get 10 new UTXOs that day. At some point you are going to have to consolidate them somehow. If on the other hand you use payjoin for all of them, in the most extreme case you just have 1 UTXO at the beginning of the day and 1 UTXO at the end of the day. Each new payment you would consume the previous payment’s UTXO as input and you get a new one coming out. We call that the snowball effect because that tends to lead to larger and larger size of UTXO. The reality is that no one is going to be 100 percent payjoins so it is going to be more complicated. It is also not 100 percent clear what the effect is in terms of the economics of how much money you spend in transaction fees as a merchant using this technique versus not. A complicated question.
It is an interesting question with the snowballing effect because on the one hand if you use the same UTXOs for payjoin transactions over and over again that might lead to fingerprinting that in fact a payjoin transaction is going on. We would know that the common input ownership is actually broken. Then it might be worse. That is worth considering. Then it is a question of coin selection of how to do payjoin. I think we have a quite good effect with using equal outputs of a coinjoin or the change of an equal value input coinjoin like Wasabi or Joinmarket. Here if both users have change from a coinjoin and then they do a payjoin the heuristic would say that both of these changes belong to the same user. That might help quite a lot with the fingerprinting of change because it would be screwed up quite a lot.
That gets even more complicated. In the example you just gave, if we did an equal sized output coinjoin together and there were 15 people in the set, 2 of us in that set took the change outputs we got and made a payjoin with them then I think we are going to give away that we have done a payjoin because the way coinjoin software works now is it only produces one change output. It wouldn’t produce two. Unless yours produces two in which case that is fair.
I’m not sure how it holds up for the participants of the same coinjoin.
I think we are getting into the weeds here with these details but maybe it is ok because these are the kind of things that people are thinking about as they go through these protocols.
Why is it worse to do this snowballing? I didn’t get it. Maybe you can elaborate on that. The sender has to provide a transaction to the receiver. The receiver does a partially signed Bitcoin transaction and sends it back to the sender? Is this right? A three way handshake?
It is two way not three way.
The sender doesn’t have to provide another output. It just has to send the transaction. The transaction is not completed from the receiver?
Why might snowball be usually bad? In this case where a merchant receives a hundred payments in one day, if he were to take the first payment and use it as input to the second payment then he would double the amount, go from 1 Bitcoin to 2 Bitcoin. Then he would use it again as an input to the next transaction where he would receive another Bitcoin. It would be 4 Bitcoin. By the end of the day he would have a single UTXO with 100 Bitcoin. The bad thing about that is that is a very obvious behavior to observe on the blockchain because usually the way the merchant works now, they are going to get a lot of transactions where the inputs are just normal sized but here you could easily trace a single UTXO being consumed and then produced. Consumed and then produced. In a series of steps. I’m not saying 100 percent because the whole point of all this is that nothing is certain in blockchain analysis. It is not like anyone can be 100 percent sure of anything from the blockchain data on its own. You don’t want to make really obvious flags like that. The second question, I understand you are a little confused about the sequence of events in a payjoin as currently implemented. I wish I had a nice diagram of steps. I think it is pretty simple. If you look at this example transaction here with Alice and Bob. How would it actually end up happening? Alice would come along and she would have these first two 0.05 and 0.09 inputs and she is planning to pay Bob 0.1. 0.05 plus 0.09 being 0.14 and this is 0.1 so this adds up to enough to pay that 0.1. She could make a normal transaction where the inputs were 0.09 and 0.05 and one of the outputs was 0.1 paying Bob and the other output would be change paying her 0.04. That would be a normal payment transaction where she pays 0.1 and she gets 0.04 back as change. Here is what she does. She makes that transaction, the normal transaction, she signs it as normal. The normal next step is to broadcast it onto the Bitcoin network. She does not do that. Instead she sends the payjoin request. She has already got a BIP 21 URI, a request from the server saying “Please pay me 100 dollars and here is the payjoin URL.” She builds that standard normal payment transaction and sends it in her HTTP request to that payjoin URL. The server only has to do the following. It has to check that that payment transaction is valid, does not broadcast it, instead a creates a partially signed transaction of exactly the type that you are seeing here. He is the one who creates this transaction. He takes the same inputs that Alice gave him, the 0.05 and 0.09 inputs, he adds one of his own inputs. Then he creates two outputs but the difference being of course that while Alice still gets her change, his output is no longer 0.1 it is now 0.18 because he added in the 0.8 that he provided to the inputs to balance it out. He creates this transaction but of course he completely sign and broadcast transaction because he is not Alice and he can’t sign these first two inputs because she owns these inputs. He creates the transaction in a partially signed state where he has signed his input here and her inputs remain unsigned. In that partially signed form, as a PSBT, he sends it back as part of his HTTP response. When Alice receives it she looks at it, does all the checks, if she is happy she signs these first two inputs, broadcasts the whole thing onto the network. If anything goes wrong at any stage in that process one of the other parties can broadcast the original ordinary transaction.
There are two transactions involved. One is from Alice. She sends a complete signed transaction to the receiver. The receiver just looks at it, evaluates the signature is true and then creates a completely new partially signed Bitcoin transaction with the amounts of the previous transaction…
It is not completely new. Maybe this is the point that is confusing you. The one that he creates still uses the same Alice inputs as before.
The same inputs but the transactions are different because the first transaction is already signed and the receiver needs a partially signed Bitcoin transaction.
Don’t forget that whatever signatures Alice had on the original transaction are no longer valid in the new transaction. I think you understand it. When you look at this kind of transaction you have in general four possible interpretations. You could interpret it as being a payment of 0.18 with a change of 0.04 and not being a payjoin, being an ordinary transaction. You could interpret it as being a payment of 0.04 and a change of 0.18 and not being a payjoin. Or you could interpret it as a payjoin, there are at least two more interpretations. There are multiple interpretations. It could be the payment is 0.1. I’ll let you figure out the details yourself. The point is that you can take the position that it is a payjoin or you can take the position that it isn’t a payjoin and that will give you different possible interpretations. It would be silly to think that payjoin somehow hides the amount totally. Obviously it doesn’t hide the amount totally. There are a fixed number of combinations of numbers here. Unless this is opening a Lightning channel and you don’t know what is going on because who is paying who? You could be paying someone on the other side of the network or it could be a coin swap and so on. If we just restrict ourselves to ordinary transactions or payjoins there are a finite fixed number of interpretations.
That leaves one remaining open question. Does anyone know or have opinions about which other wallets should or could or will implement this? In a minute I am going to show you the result of my implementation and a discussion of fingerprinting.
Electrum should implement it.
I agree. Has anybody heard from them about it? I’ll ping ghost and see if anyone can tell me anything.
Green wallet. Blockstream had a blog post to kick off all this. They have been sponsoring BTCPay to use the standard they were proposing. It would be quite logical to get into Green wallet as soon as possible.
This brings us nicely into the topic of fingerprinting because unless I missed something, Green wallet’s inputs are distinct.
They would be a 2-of-2 multisig if they used that feature. But you don’t necessarily use that feature in Green wallet, it is optional.
I actually have Green wallet and I use it fairly regularly but I never bothered to look at the actual transactions. I thought it was either 2-of-2 or 2-of-3 or is there an option to have single key?
It is either 2FA or Blockstream has the other key.
Are you saying that you think it is perfectly reasonable that they could implement payjoin between Green wallets. I don’t think it would work with a merchant because the merchant would not have the same kind of input scripts. Am I missing something? What I raised on this post is I made my second experiment… I had a BTCPay Server remote instance that I chose. I paid him. I made this payjoin transaction using my Joinmarket wallet. If you are confused Joinmarket does have native SegWit wallets, it is just that we don’t really use them because you can’t use them in the equal amount coinjoins. This is from a Joinmarket wallet to a BTCPay Server instance. It produced this transaction. First of all let’s look at the amounts. Is there anything that stands out in the amounts? I was asking a trick question. Unless you are talking about what is already highlighted here in blue (Unnecessary input heuristic) there isn’t really anything remarkable about those arguments. blockstream.info, this particular block explorer does mark rounded amounts. There are no rounded amounts here. I chose a random payment amount. That was my choice because it was a donation server, I could choose a payment amount. The amounts don’t give away very much. Indeed you would be hard pressed to figure out how much money I donated just by looking at these numbers. You could come up with a list of possibilities. The interesting thing is if you actually expand the transaction and you look at the whole serialization here there is something that might stand out about this transaction, maybe two things. The first one is the unnecessary input heuristic that we discussed earlier. Let’s say this was an ordinary payment of 0.017. Why would the payer produce 0.0179 and 0.00198? What would be the necessity of adding this extra input. You might say for fees but that is already quite a bit bigger than needed for fees there. That is one version of the unnecessary input heuristic. This does flag that. The interesting thing is that quite a lot of ordinary transactions flag that heuristic anyway. I’m quite interested to talk to Larry or whoever it is runs this block explorer nowadays and ask them how often they make this flag. I think they are going to make that flag attached to transactions quite often. I’m not sure if it is going to be 20 percent or 50 percent but I would like to know. There is one other thing about this transaction that flags. Can anyone see it?
I have a guess. The nSequence number.
Very good. Did you hear about it recently or did you see it just from looking at it?
I see it.
This is different. Do you remember what the significance of these particular values is?
0xB10C wrote an article a couple of days ago about the nSequence number for timelocks. It is about the fee sniping attack if I am right.
It is related to that for sure.
It means I think that you only can relay transactions with the current block height.
Something like that. Obviously we are not going to have an hour lecture on the details of Bitcoin logic here. Let’s give a few key points. First of all, this is the maximum integer in 4 bytes and this is the default nSequence number. It indicates that the input is final. In some vague sense that meant something to Satoshi back in the day but it has evolved in ways that a lot of people can barely understand, it is a bit weird. But if a transaction is final in the sense that all of these values are the largest integer
0xffffffff then the nLockTime field in the transaction which is shown somewhere or not? The nLockTime field is disabled. If you want to use a nLockTime value the problem is that if you have all the sequence numbers at that maximum value it won’t be enabled. So what Bitcoin Core does is use this value
0xfffffffe and this value is one less than the maximum integer. It has
e instead of
f at the end so it is one less in hex. That value enables the nLockTime field. If it is enabled you can do an anti fee sniping measure which is you set the locktime to a recent block and that helps with avoiding a miner attack. Can anyone explain it?
I believe this is not exactly correct. The problem with a fee sniping attack is if there is a block confirmed that has spent a lot on fees a miner has an incentive to mine a parent of that block and reorg. The difference in the fees relative to the current state of the mempool is what they would gain by doing that. Maybe that is enough to offset the block subsidy and the difficulty of actually mining two blocks and succeeding in a reorg. But that would be fixed by setting the nLockTime field.
There is a connection between the two. You want to set the nLockTime to fix this problem but you can’t set the nLockTime and also set all the nSequence values to
0xffffffff. It disables it. We went over this yesterday because we were discussing this point in the BTCPay Server Mattermost. I can recommend this particular StackExchange discussion that helps to explain the rather complicated relationship between nLockTime and nSequence. There is the question of what the actual rules are. Then there is the question of what does Bitcoin Core usually do? That is relevant because a wallet like Joinmarket might want to copy what Bitcoin Core usually does. There is the additional complicating factor of whether you are setting the opt-in RBF or not. To set that you need to set this nSequence field to max int minus two where this is only max int minus one. In this case RBF is disabled. There are a whole bunch of complicated rules and considerations here. The upshot of a long conversation I had with Nicolas Dorier yesterday was that he agreed that we should have the server wallet agree with the sender or the client wallets setting of nLockTime. In Joinmarket’s case I always have this nSequence value set to enable the nLockTime. His server, his NBitcoin code was by default sending out the maximum integer always. The reasons are complicated. He has updated this now apparently. What is the point of going into all these details? I just wanted to explain to people how tricky these questions are. If we are going to try to have a steganographic transaction we are going to have to try to make sure that the servers should absolutely mimic the sending wallet. The sending wallet could be a mobile wallet like Green wallet or Electrum or whatever. My point is that I think the servers have to mimic the clients. Even that statement is not quite clear enough. The protocol should be really clear on these points I think.
It is good that you bring this up. BTCPay Server already does this with the address types which is another way to fingerprint this. If the receiver initially proposes a native SegWit bech32 address but the sender spends a coin and generates a change address that is legacy then in the payjoin transaction the server will respond and switch his receiving address at least to a legacy address. I’m not sure if they also select a coin if its legacy. That would be optimal too.
I want to expand a bit on those points. First of all we are mostly concerned with the inputs not the outputs because it seems to me that having more than one scriptPubKey type in the output is not really a problem because it is very common for a sending wallet to have a different scriptPubKey than a receiving wallet. That is one of the ways you identify change outputs so maybe you are right. But it is the inputs that really count. The thing is that BTCPay Server as I understand it from speaking to Mr Kukks the merchant wallet does not yet have the ability to support multiple scriptPubKey types as once. You could have a P2SH-P2WPKH wallet in BTCPay Server merchant or you can have a native bech32 BTCPay Server merchant wallet. I don’t think you can currently have one that mixes both types together. That would be ideal for our scenario because we want to allow different sending wallets to get payjoins successfully through and not have to fallback to the default. Absolutely a scriptPubKey is a huge issue as well. This is the minutiae, the nSequence but you have to get it right.
This tells you where Blockstream are flagging it. I am curious whether they are using what we called one or two but I’ll read that later.
Both heuristics are used from what I saw. This is a shameless plug to please review my tiny documentation Bitcoin Core PR where I have commented about nSequence. If you look at the diff it is quite simple, it is just a comment change. nSequence is so confusing.
I think it is confusing because the original concept behind it was not something that ended up being… A complicated question. I think the most important point there is that there is a connection and nLockTIme and nSequence. Nicolas Dorier didn’t get it at first either and I certainly wouldn’t have got it unless I read up and researched it. For me I wouldn’t usually think about stuff like this. It all came out of the fact that I was really keen on copying the anonymity set of Bitcoin Core and Electrum in Joinmarket’s non-coinjoin payments. Obviously it is a wallet so you have to the ability to make ordinary payments. In that case I want to be steganographic. In the equal sized coinjoin outputs in Joinmarket there is no possibility so we don’t even try. Because I did that that is why I did this particular combination of nSequence and nLockTime in Joinmarket and that is why when I did this payjoin with BTCPay Server because their logic didn’t agree precisely with mine we got this fingerprint.
To mention about the nSequence. Many people prefer to signal replace-by-fee (RBF) because they want to be sure to make the transaction confirm in the timeframe that they want. That is something that can fingerprint the wallet that the user is using.
What is even more confusing is that I believe that you only have to set the RBF flag in one input? You don’t have to set it all of them do you? Is that right?
If you flag one input then the transaction and all of the child transactions are replaceable. Of course you can double spend the input that you flag.
The reason I mention it is because when I was going over this with Nicolas Dorier yesterday we were trying to figure out exactly what we should do, we had to worry about what if the sender is not using the same nSequence in every input. This is really in the weeds. Perhaps we should stop with all that extremely detailed stuff and stick with the more high level time for what remains.
I think this is an interesting document. Let’s go back to the highest level again. This is a gist from LaurentMT who a lot of you will know he is employed by Samourai. He is an analyst who has been working on blockchain analysis and privacy tools for ages in Bitcoin. He is giving some opinions here about payjoin transactions and deception tools and the concept of steganographic transactions that we have discussed today. He is addressing this issue of fingerprinting that we just talked about. He is saying “If you want to avoid having fingerprinting you can go in two directions. You can choose to have everyone have exactly the same fingerprint or you can go in the direction of having fingerprints be random all the time.” I don’t think I agree with everything he said but I agree with a lot of what he said. He is arguing that randomness is better and more realistic because it is very hard for every wallet to be the same and have the same fingerprint in every detail. What happens when somebody wants to update their software and so on. I think that is largely true but I think a couple of additional points I’d make. First of all we don’t have infinite scope to be random of course. The trivial example is you want RBF you can’t just use maximum sequence number. The other point I’d make is that while it is true that if we are just talking about a big network talking to each other then it is hard for everyone to agree with the same fingerprints. I would argue it is a bit easier for servers just to mimic and agree with the fingerprints of their clients. It is not necessarily that easy because different scriptPubKey types are a bit of a hurdle. We can’t really have payjoins where the server produces a native SegWit input and the client produces a legacy input. I do recommend reading it though. It has got some thoughtful points about different ways we might be building wallets with steganographic transactions in mind.
Should the RBF flag be truly random in the sense that it is a 50/50 chance between RBF signaling or not RBF signaling. The downside that I see here, we talked about this at Wasabi, is that currently 0.6 percent of all native SegWit transactions signal for RBF. If Wasabi went for 50/50 then there is a pretty high likelihood that the RBF transaction is from Wasabi. To work around this what we have done is on average 2 percent of transactions will signal RBF. It is still randomly chosen but with a skew towards the actual usage on the network. Is that useful?
If you asking me my opinion as I already said while in principle complete randomness is a more practical option than complete homogeneity, I was musing about this on Mastodon earlier today. I was saying on the one hand you could have a completely blank sheet of paper and on the other hand you could have a sheet of paper with masses of random pen scribbles all over it. In both cases no information conveyed. If we go to the massive amount of scribbles, have everything activated randomly it is highly problematic because as an example your wallet might really think that RBF is a bad thing or RBF is a good thing and might really want to have that feature. You can’t really randomize it unless you are going to give up what you are trying to do. Your question is another point that often gets raised which is we want this thing to be random but if we are trying to do randomness for the purposes of steganographic behavior then we have to try to match our random distribution to the existing distribution on the network. Then you get these feedback loops. It is possible but it sounds damn complicated. I really feel like the problem is a deep one so that it may be the practical reality is a certain degree of randomness is the best we can hope for and we can’t have perfect randomness. I also think that in this specific case of this two party protocol the practical reality is that we have to focus on the idea that the receiver mimics the sender’s behavior. It won’t always be possible to get it right. That is ok. Let’s not forget that 10, 20 percent of payjoins on the network, if it is known to be 10, 20 percent realistically is more than enough because it is going to screw up any realistic assumption mechanism.
There is another aspect of this which is something I find both deeply fascinating and troubling. The concept of a Schelling point from game theory. There is always a dilemma between your behavior as an individual trying to protect your own privacy and an individual acting as part of an ecosystem where privacy is an issue. If we assume that fungibility is emergent from privacy and that is a desirable property of money it is very unclear what is the optimal behavior? If everybody could just agree to stop producing fingerprintable transactions and actually do it that would be great. There is a huge technical barrier there. There is always a dilemma as an individual interested in Bitcoin’s success and their own personal privacy, do you make sacrifices like participating in coinjoins which is an overt political act? It sends a message to the world at large. Or do you try to blend in to the crowd and mask yourself?
I don’t think that bifurcation is quite right. Doing equal sized coinjoins I agree is an overt political act and could be in some circumstances a sacrifice. But you can’t argue that it has no value at all.
I’m arguing it has tremendous value.
I mean specifically in creating privacy for that user.
That could also mean that you are banned from various exchanges.
It is a sacrifice but you can’t argue that there isn’t a positive effect specifically for that user.
Sorry if I implied that, that wasn’t my intention at all. I didn’t want to get into coinjoins, that was just the easy example of an obvious trade-off. In the context of this stuff that further complicates all of these discussions. I have nothing really to say practically speaking it is just that the nuances here are not just technical it is also about how people perceive these technical details.
I totally agree with your point. When I was on Stephan Livera I was really hammering that Schelling point home. I’m trying to look at it in a positive way. There is a Schelling point today that Bitcoin transactions are traceable and indeed that doing anything apart from ordinary transactions looks suspicious. I think payjoin could be part of the way we flip that Schelling point. If we have techniques that make it not only normal but easy to slightly improve your privacy. Ideally they would be of economic benefit. We haven’t discussed that today. It is a very interesting question about what is the economic impact of doing payjoins as opposed to ordinary transactions. Ideally we could have techniques to improve that like signature aggregation. But even if we don’t have economic benefit, even if we have some very minor privacy benefit and it is steganographic or it is opportunistic so it is very easy to do. Maybe we could flip the Schelling point in the sense that we make blockchain analysis very ineffective to the point that it looks stupid to try to claim you are able to trace all this other stuff. Then maybe it is not so weird to be someone who does coinjoins or whatever.
I think it is more than that. It is absolutely critical because we already know objectively today that this analysis is not very scientifically rigorous. There are all these examples, I think the most prominent one is from the Dutch court system where chain analysis companies were used as expert witnesses. From the point of view of law enforcement what they would like as a service from these companies is a clear cut coherent narrative for non-technical people and they don’t really care if it is pseudo-scientific. So ambiguity which somebody with intellectual integrity like a proper scientific analysis would do is not part of what is being sold as a service by these companies. This is why it is on people who actually care about Bitcoin succeeding to really be conscious of not just looking at this at a technical level but also in terms of the perceptions. I think payjoin is a fantastic development in this regard because it confers this plausible deniability to your activity. Another interesting one in this regard is the multiparty ECDSA stuff.
Can somebody help me understand why a random RBF flag makes sense? I didn’t get the point.
Just the idea of blending into a crowd. The general idea is if your wallet has one specific behavior like a specific flag like RBF… I think a lot of people have probably heard of the concept of browser fingerprinting. The idea is your browser gives away a bunch of data about the version of Mozilla and of the screen dimensions, I don’t even know the details. Cumulatively a lot of little pieces of information together might make it very obvious who you are. This is kind of similar, maybe it is not the same. If your wallet uses four or five different flags consistently in a certain way it might be obvious that that is the same wallet. Whereas if you randomize some of those flags, from one spend to the next spend, one uses RBF and the next one doesn’t, it then may not be obvious that it is the same wallet.
Regarding the scriptPubKey compatibility what do you think if the sender creates a disposable 1 hop to a “correct” type of the receiver and then using that to have the correct fingerprint?
Is he saying that somebody who is a sender in the payjoin protocol might just for the payment create a different scriptPubKey so that it matches the server? Is that what he means?
I think what he means is if the sender proposes a legacy address or whatever the fingerprint is and the server only has native SegWit bech32 address coins then he spends a SegWit native coin, a one input, one output transaction, where the output is a legacy address and then he uses this unconfirmed transaction output in the payjoin proposal. Then at least for this individual payjoin transaction both inputs are with legacy addresses. Of course if you look at the grander scheme of things, especially timing analysis, you would see one of these coins was done in this spontaneous self spend.
Who is creating the one time address in order to match up? Is the server doing that? The server. Interesting thought, I’m not sure.
How far is the payjoin integration in Joinmarket? What was the previous work and how has this changed with the new proposal?
The previous work is something that happened in early 2019. That allowed two Joinmarket wallets to pay each other with a payjoin. Same basic protocol but not over HTTP and no server, just two Joinmarket bots talking to each other over an encrypted message channel. The new version, I think I explained before the call started, is a situation where in order to implement it I had to build it on some rearchitected Bitcoin backend using a fork of python-bitcoinlib in fact. I built that in a branch in order to get PSBT functionality which we now have in that branch. Also I put SNICKER functionality in there but it is not really user exposed anyway. That is shelved for the moment. I was able to build this new BTCPay Server payjoin code in that branch. It is a pull request 536 in Joinmarket. You will see there where I have got to point where it is all finished on the command line at least. I was able to do experiments sending. If there is anyone there who is somewhat developer inclined and wants to help out they could use that pull request branch 536. Make sure to install libsecp256k1 locally because there is a technical installation issue there that is going to get resolved soon. That can get merged in a few weeks. The code is already functional, I have been testing it with mainnet. For receiving we would probably have a similar situation to Wasabi, we would probably like to set it up so that a user can set up their own ephemeral hidden service temporarily to receive a payment using the same protocol as BTCPay Server uses today instead of this old method that we had which is proprietary to Joinmarket. I think it would be nice if all the wallets that are currently implementing the sender side at least try to receive the receiver side using hidden services. It is not necessarily that convenient for users to do that but at least that is very generic and it should function for most people if they want to use it.
It is awesome to know that Joinmarket is going to implement the receiver part. Payjoin is when two people to collude to fool a third one, a blockchain spy. In order to be able to do this they need to be able to communicate. That is where the endpoint part comes from. This works but in order to make it work even more we need the wallets like Joinmarket for example to implement the receiver part. Wallets that are run on small devices or wallets that for some reason need to be on for a long time, I will implement this in Wasabi too. This should be a peer-to-peer way to introduce an enormous amount of confusion into the blockchain so we need this. If the wallet runs for a long time it is a good candidate and if it is not because it is just a mobile wallet, it just implements the sender part, it is ok, you are still contributing to the privacy.
I have primitive server code, I think we can do the onion receiver part but we need to get the sender part in first, get it into master and then into a release. It is going to be a few weeks. Regarding BIP 69, in a discussion with Nicolas Dorier about this issue of how we do we make sure that payjoins don’t fingerprint it made me realize that the fact that some wallets use BIP 69 is actually problematic for this. The thing about BIP 69 is that it is ordering inputs and outputs lexicographically, alphabetical but with characters. That is all very well and good because it is random and it is deterministic random but the thing is it is identifiable. Because it is identifiable it is a fingerprint. Unless everyone was going to use BIP 69 but the presence of BIP 69 is a net negative for us trying to make transactions not stand out. Nicolas Dorier originally had the intention to replicate BIP 69 in the payjoin transaction if he sees it in the original fallback transaction. I was saying that is a problem because there are plenty of false positives. A randomly shuffled transaction which is how a lot of wallets do it including Joinmarket can sometimes look like BIP 69 by accident. If you replicate that you are going to change the distribution. These are the nasty details you get when you choose to add semantics into the transaction construction which is not intrinsically needed to be there. I have been convinced by this discussion that BIP 69 is a scourge on Bitcoin and should be eradicated at all costs.
So SNICKER. What is the latest on that?
I am thinking about it from Joinmarket’s point of view because other people don’t seem to be interested in implementing it right now. I have put the backend infrastructure in place for it. Of all the cryptographic primitives I even have a receiver script for it but if I was to actually make it work even in the restricted case of using it with Joinmarket participants it would work really well because it would be easy to find the candidate transactions but either I or someone else would have to set up a hidden service and start scanning and writing scanning code and writing proposal code, a bunch of work. I haven’t got any time to do it now. If I had nothing else to do it I would do it because I think it is really interesting. But I think other people don’t seem to have decided that it is sufficiently interesting to put a lot of time and effort into building infrastructure for it. We’ll see. I still think it is way better than people think but I have been saying that for two years.
You are more confident in it now? It has been a few months since the mailing list post.
I didn’t really change my mind about it. I think it is one interesting feather to put in your cap. It is another technique to add to something like Joinmarket which is trying to be a multifaceted Bitcoin wallet as much as it can, focused on coinjoin. It would be nice to add it there. Maybe some other wallets too. In the long term it could conceivably be something added to mobile wallets but there doesn’t seem to be an appetite to do that right now.