Skip to content

Instantly share code, notes, and snippets.

@gavinandresen
Last active February 13, 2020 18:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gavinandresen/aeed66e7e23c905f885362e6fbe3a81d to your computer and use it in GitHub Desktop.
Save gavinandresen/aeed66e7e23c905f885362e6fbe3a81d to your computer and use it in GitHub Desktop.
Smart contract design for handling "change" privately

Brain dump on "Thresher", inspired by playing with tornado.cash on ethereum (see http://gavinandresen.ninja/a-more-private-eth-wallet ). "Thresher" because it accumulates deposits until some minimum threshold is reached, then pays out.

The problem: tornado.cash deposits and withdrawals are fixed-size, with a minimum size (0.1 ETH in the case of ether). Anybody that uses tornado properly will accumulate less-than-minimum amounts of ETH in different addresses and be unable to spend them without compromising privacy.

Note: I'll be talking only about ETH for the rest of this gist, but the idea obviously applies to any of the tokens supported by tornado.cash.

Solution: a smart contract that accepts deposits that are less than 0.1 ETH with a tornado.cash 'note'. Once the contract has accumulated 0.1 ETH or more, it redeposits 0.1 ETH into tornado.cash with one of the deposit's notes, picked fairly at random (e.g. if you deposit 0.09 ETH your note has a 90% chance of being picked).

I'm going to make some people cringe and propose using a "good enough" way to pick the winners:

Winners are picked as a side effect of processing a new deposit at some current block height N.

The hash of block N-1 is used as the random seed to pick a winner. However, to make cheating by miners even more costly (they must pay transaction fees to another miner to get their entries on the list), only deposits received before block N-1 can win.

See "On Bitcoin as a public randomess source" by Bonneau, Clark, and Goldfeder for an analysis of miners trying to cheat by throwing away winning block hashes: https://pdfs.semanticscholar.org/ebae/9c7d91ea8b6a987642040a2142cc5ea67f7d.pdf Cheating only pays if miners can win more than twice what they earn mining a block; the reward is currently 2 ETH (plus fees), so we're OK using the block hash as our randomness source as long a cheating miner can't win more than 4 ETH.

Can depositors cheat? They cannot make it more likely that their entry is picked, because they cannot know what the block hash will be.

Gas costs

Tornado deposits are somewhat expensive-- on the order of a million gas. Each deposit to Thresher should include enough gas to cover a deposit, because each deposit could pick a winner and call tornado.deposit(). The contract could be written to pick multiple winners and make multiple tornado deposits at once (for once call to deposit()) if it accumulated 0.2 or more ether, but that would be bad because it would make the amount of gas required very unpredictable.

A Thresher user could try to cheat on gas costs by looking at the current state of the contract and seeing if their new deposit is likely to trigger a 'win' and a tornado deposit (e.g. most simply if their deposit will cause the Thresher balance to be >= 0.1 ETH). If it will (costing ~1million gas) they could wait and send their transaction later, after some other deposit has drained the contract.

@gavinandresen
Copy link
Author

gavinandresen commented Feb 7, 2020 via email

@kaibakker
Copy link

That sounds good to me.

Looking at line 159. Am I right that it is possible to always win by adding a deposit and wait for over 256 blocks. Because winningThreshold is set to 0.

It also seems to be possible to get stuck behind a too big winning transaction in the same block. let's say there where 2 deposits in the same block, both of them should win. But the first winAmount is larger than the current balance, than the second transaction doesn't have a change to get their money. Maybe you want to check if the winAmount is smaller than the contracts balance at the time of the deposit (before or after the previous deposit is handled within that same transaction).

@gavinandresen
Copy link
Author

gavinandresen commented Feb 13, 2020 via email

@kaibakker
Copy link

Nice Gavin, I get it now.

I would be okay with having a small chance of not winning because the contract balance gets too low, but it would be nice to minimize this chance when there is like 1 other entry in the queue.

I imagine this to have some web interface in the future, where every deposit is represented like a raffle ticket with all information on it, turning red or green based on if they win or lose. Maybe I have some time in the future to play around with this.

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