Skip to content

Instantly share code, notes, and snippets.

Last active May 26, 2020 19:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chris-belcher/c8219fe6283b8f97116aeccaf1190c07 to your computer and use it in GitHub Desktop.
Save chris-belcher/c8219fe6283b8f97116aeccaf1190c07 to your computer and use it in GitHub Desktop.
JoinMarket with off-chain fees

JoinMarket with off-chain fees


Problem: Single JoinMarket coinjoins aren't private enough

A single JoinMarket coinjoin often doesn't hide which inputs belong to the maker(s) and which belong to the taker. This is because the coinjoin fee is included on-chain.

To tell apart takers' inputs from makers' inputs, subset matching can be used. The taker's subset is the combination that loses value. The makers' subsets usually increase as they collect a coinjoin fee from the taker. Sometimes this kind of subset matching is not possible, especially when a lot of makers are used. The Mosor 2016 paper has an algorithm which succeeds about 2/3 of the time and someone on bitcointalk has a script which succeeds 24% of the time. So such unmixing often possible to do, and it's a problem.

JoinMarket today can provide privacy only if coinjoins are chained.

For example, consider selling bitcoins in exchange for cash to someone. After sending them a coinjoin with today's JoinMarket the other person could figure out which inputs are yours and so find a lower bound of your wealth.

Solution: Off-chain JoinMarket fees

Paying coinjoin fees off-chain is a solution.

There are many off-chain payment solutions in bitcoin, the most practical for this use-case is probably chaumain ecash servers.

Lightning Network potentially has some privacy issues, these are not very well researched yet and it wouldn't be good to bet everything on them. We wouldn't want to reveal things about the makers. Another reason to choose chaumain ecash servers over LN is that the former can easily do any smart contract, which is needed for the atomic trade of money in exchange for coinjoin. In this off-chain-JoinMarket the ecash server(s) would be coded to only give the money to the maker(s) if a certain txid gets confirmed. This is not possible with Lightning Network I think.

The user experiance for a taker would be that before using JoinMarket they deposit some coins into an ecash server and receive ecash tokens. They then initiate a JoinMarket coinjoin transaction and pay some of the ecash to makers, but the makers only receive the ecash if the coinjoin transaction gets confirmed, otherwise the ecash is returned to the taker after a timeout. The maker yield-generators are coded to periodically withdraw funds from the server.

The depositing and withdrawing of money from the ecash server can also be done over Lightning Network.

The on-chain coinjoin transaction should have every subset (taker and makers) paying the same amount in miner fees for privacy; but the taker could reimburse the makers off-chain if, like today, the taker pays most of the miner fees.

The ecash server(s) would have to be run by some people and would be part of the JoinMarket infrastructure, like the IRC networks are today. Also they would have to be trusted to not steal the money. And they would probably require a bit of effort to code. Ideally they would have some mechanism to sell their reputation otherwise they would be incentivized to exit scam to convert their reputation to money.

Appendix: Avoiding an attack by the taker

If the server returns ecash fees to the taker after a timeout, then there is a possible attack.

A possible attack is that the taker pays the ecash to the server, the obtains the fully-signed transaction from makers but doesn't broadcast it. After the timeout is over the taker will get his fee money back, they can then broadcast the transaction which will confirm, and so get a coinjoin without paying for it.

To avoid this, the contract has to work a different way. The taker gets their money back after a timeout but only after one of the coinjoin transaction inputs has been spent in a different transaction. This fixes the above attack because the original coinjoin becomes invalid.

For the user experiance, if a taker pays for some maker fees but one maker doesn't reply then the taker will have to start the joinmarket protocol again with new makers while still having their original ecash maker fees locked up. But once the taker succeeds in getting a coinjoin fully-signed and broadcast then they will get all their locked up fees coming back to them. So the user experiance is not too bad, except the taker should have ready 3x or 4x more maker fees than required for a single coinjoin.

Copy link

nothingmuch commented Jan 17, 2019

possible protocol, which avoids de-anonymization risk (server identifying taker's input, and server linking tokens which have been returned):

first, obtain clean e-cash tokens. only clean e-cash tokens can be used to withdraw (via LN, on chain, w/e).

clean e-cash tokens can be locked to fund a proposed transaction, in exchange for a signed receipt confirming the reimbursement amount, which the taker provides to makers. the lock is in place until the transaction is confirmed or chain or it expires (e.g. by an input being spent).

if the transaction is confirmed, the token is considered used. clean tokens can be obtained as reimbursement by submitting receipts signed with the keys of the transaction input. to avoid DoS concerns from the server's PoV, the redeemer can be burdened with proving a conflicting transaction has been included in the blockchain.

if the transaction was invalidated, the token is unlocked. unlocked coins can only be reissued in exchange for clean tokens to avoid linking takers to specific tokens. (in the above attack scenario - why is broadcast enough? couldn't the first tx be re-broadcast if a conflicting tx is not confirmed and eventually removed from mempools? i don't see a reason why locked tokens can't be double-spent on conflicting transactions, as only one of them will end up redeemable, apart from that this would likely allow the server to identify the taker's output input)

lastly, it's worth noting that multiple servers can reimburse the same transaction, in order to spread risks.

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