The power of Payjoins

Two years ago, we had the initial discussion about payoins here PayJoin (P2EP) in Grin, but I don’t think we went too deep into them at that point. I thought I’ve already written a document about why I believe payjoins are great, but I wasn’t able to find it so I decided to write my thoughts down to have them somewhere. As most of you know, I think payjoins would make a better default transaction than the transactions we currently have. I tried to capture why that is in this document About Payjoins. The most important thing to take from this is that it’s much more complicated than “the receiver leaks input, I don’t want to do that”. I think payjoins by default may be seen as a potential strategy to improve the overall fungibility of the outputs on the network.

Any feedback is welcome.

Edit: I have not done any analysis as to what the chain of receiver inputs mean. This would largely be avoided if the input was prioritized to be an output created in a coinswap. Still, it would be good to have some analysis around this.

P.S. I have not left anything out intentionally, if I missed something important, let me know, I’ll try to digest the consequences of it and update the document.

2 Likes

Thank you for the write up.

Would this affect the ability to select which inputs to use in a transaction. Like today in Grin++ I can choose which of my outputs to spend. Would I then need to leave that decision up to some math that chooses which ones will fit into the equation.

Also I’m reaching here with the limits of my understanding, but what if no sum of a combination my outputs and the sender’s outputs form a working payjoin? Do we default to no payjoin or do we generate self transactions to create the necessary outputs? Or what if I have 1 output and the receiver has none to contribute?

Coin selection can be done even if payjoins are by default. Instead of algorithm picking an input, you pick it yourself.

That’s close to impossible. Consider the following transaction

5 -> 4 (change)
  -> 1 (receiver)

We can make this a payjoin with

5 -> 4 (change)
X -> 1 + X (receiver)

Note that regardless of what amount X is, we simply add the amount to both the left and right side of the equation. The reason I said close to impossible is because there are fees to be paid. If the receiver has a 0-value input, then they’d have to receive at least for the fees of one input or they’d go negative. This would of course be up to the wallet to check that the receiver never pay more than they receive.

I wouldn’t generate outputs just to payjoin - outputs are the most costly in terms of fees, I don’t think we should put that much cost on the user by default. I would default to payjoins and if the receiver has no available input to contribute, we end up with a non-payjoin transaction.

2 Likes

Note that it’s payjoins that allow for the possibility of the receiver paying the fees for their own inputs&outputs, and for the fees for the kernel to be split. It’s still possible to charge all fees to the sender (and also possible to charge them all to the receiver), but split fees seem like a sensible default for payjoins.

It’s not obvious, but I think that’s not really correct. In the current contract code, the receiver pays for their inputs, outputs and kernel regardless whether the transaction is a payjoin or not. This is possible because the receiver gets v coins, so the receiver output they create holds the amount

v - fee(n_receiver_inputs, n_receive_outputs, n_kernels)

This works even when n_receiver_inputs is 0. The receiver can always pay for the fees from the receiving output, similarly, the sender can always pay from the change output. This is what I would default to regardless whether the transaction is a payjoin or not because it’s consistent across the transactions.

1 Like

That’s true; but I thought the current wallets allowed the receipt of single nanogrins, by making the sender pay all fees.

1 Like

I suspect they do and you’re right that the logic I described changes this. There’s value in both. I’d default to each party paying for themselves with an additional option to specify custom fees from the wallet on the setup phase. This way, the sender could add fees for 1 input and 1 output for the receiver and the receiver would specify 0 as custom fees making the sender pay for everything.

For obfuscation against an outside observer every discriminable transaction category must turn up in every category the outside observer tries to distinguish from. I like if many different transaction types become (keep being) possible. But also I think 2in2out pay-join with split fees is a good proposal for default. I think market should decide if this fits for most transactions.
One Idea that I would like also for donation, would be encrypted output secret sharing through grin-addresses and slatepacks. If some outputs are shared to other users about multiple hops, not even the original output-creator needs to know who spend it for example in a pay-join.

In a payjoin transaction, the sender and the receiver can no longer be distinguished. In a 2-2 transaction, you can think of both inputs as being the sender that created a “change output” because both parties get an output. There’s no difference between the sender and the receiver on chain because a transaction/chain doesn’t have amounts - at least not for anyone that was not involved in the transaction. If you have all transactions payjoins, from the chain perspective, it’s no different as if you were always the sender with a change output. This also means you are building a chain of change outputs regardless if you’re sending or receiving coins. It’s not clear to me yet what the privacy consequences of that are. It would be a cool thing to research the implications of this e.g. how bad are the output change chains when put to extremes?

I guess that’s an open research question for anyone interested in this.

The observer would assume the secret wasn’t shared because this can’t really be visible unless they also received the secret and there’s incentive to spent it asap because sharing the secret with more than 2 parties is basically an offchain double-spend and you want to take ownership of the coins with an on-chain transaction.

For every output there could always be an upper bound and a lower bound grin value associated. For miner rewards upper bound and a lower bound grin value are equal since coinbase and fee are known. I think iterative the associated Limits for each output could tighten if other associated outputs bleed information. That could be added to block-explorer.

Note that it’s possible to have multiple coinbase outputs, making only their total value known.

For (non-coinbase) payjoin outputs, the lower bound is 0. While the upper bound will grow with every successive payjoin and every coinswap, potentially at an exponential rate. In this way, both payjoins and coinswaps hugely increase the uncertainty in output values.

5 Likes

Might Payjoins have impact on fungibility in regards of double spend vulnerability? I Think every Payjoin that depends on new Coinbase output can’t be replayed on an other chain. So a receiver is more threaten by reorgs. This gives incentive to re-spend new received Payjoins fast if they come from a spender that has not the reputation to recreate them if necessary.

Coinbase outputs are locked for a certain period of time. Whenever the receiver receives money from an input, if that input is reorged, any graph from the outputs of this transaction can’t be made. This is regardless whether the input is a coinbase or a regular input. I hope I understood your concern right.

1 Like