Imagine a system where a user can move funds into and take an arbitrary amount of funds out with having a plausible explanation where the funds came from.
Introducing Zero Knowledge Lottery (ZK-lottery). A protocol that allows users to participate in a lottery and win money without leaking any information about the outcome; only the participant knows what was the payoff! Outside observers, the casino itself and the other lottery participants have no information whether you won or lost. Only you know.
How is this even possible?
To start the explanation, Tornado Cash Nova is a good base for this protocol. Tornado Cash Nova is a protocol that allows users to:
1) Deposit an arbitrary amount into a contract
2) Move funds from a participant to participant inside the system privately (thanks to zero knowledge proofs)
3) Withdraw your funds privately in smaller denominations out of the contract
Now let’s take a look at how to add the ZK-lottery on top of these features. On deposit, we require user to provide some additional information along with their zero knowledge commitment:
1) A future blockchain block number when they prefer their lottery to be held
2) A guess for the random number that they think the lottery will result in
3) A difficulty number indicating users’ risk preference. A higher difficulty will result in a higher chance to lose all the funds but will result in higher possible payouts, while a smaller number will result in a higher chance of getting back approximately the same amount as the user put in.
After such a deposit transaction. The user cannot withdraw their funds until the asked block has passed. At the block, a random number is generated and the win amount is determined based on the difficulty, users guess and this random number.
As the system has no information on when we need to generate a random number, we need to have another system that is constantly generating random numbers. After Ethereum’s The Merge network upgrade, one such random number generator is RandDAO. While other random number generation schemes could be used, this would be the most handy for us. We also need to be able to prove on chain that a such random number was generated at the given block, without revealing which block was used.
After the lottery number block has passed, the user is able to withdraw their winnings at any time. In the unfortunate case that the lottery resulted in a loss, the user is unable to withdraw.
To withdraw funds, the user sends a zero knowledge proof to the contract:
I know a secret number, nullifier, lottery number guess, lottery difficulty number and a RandDAO evaluation at the lottery number block that results in me being able to withdraw a certain amount of funds.
It’s important to design the lottery profits in a way that it would mask all the users together in order to ensure everyone’s anonymity. The system would force everyone to participate in the lottery, but users with lower risk preference could choose a really low difficulty setting. This maximizes their odds in getting their funds back. Eg, put in 1 Ether and get 0.95–1.05 Ether out of the contract. Technically it’s not necessary to force everyone to participate in the lottery as it’s possible for users to withdraw their funds in any denominations that sum up to their total deposit. However, forcing users to withdraw a random looking amount increases privacy as users tend to be lazy and withdraw all their funds at once.
On top of Tornado Cash Nova’s features, we also want a support feature to re-gamble one’s earnings again. This allows gamblers to keep playing the game without exiting the anonymity pool at all. Implementing the re-gamble feature is relatively simple to implement in the UTXO model that is already present in Tornado Cash Nova.
Statistically burnt Ether
ZK-lottery has multiple interesting properties. The system does not know if it has enough money to pay out to every participant. While we know how much users have deposited in total, we don’t know how much they have won or lost. The only way to figure this out is to have all players collaborate together and reveal how much funds they have in the system. It’s quite absurd that a casino does not know how much it owes to players, it just knows how much money it has in total. One could argue this is sloppy bookkeeping, but I call these the most decentralized company books out there.
This property is interesting as it’s possible that the contract is underwater for a very long time, but nobody would know. The system would act as a fractional reserve. In order to avoid ending up being underwater forever, we need to generate profit over time, just like any other Casino; we need a house edge. Initially we need a bigger house edge to gather up enough buffer for rewards, but later we could operate with a small house edge. We can also design the casino to automatically adjust its risk parameters (deposit size and such).
The house edge leads us to another interesting property. Statistically all the profits generated in the system are burnt. The funds are not technically burnt as the funds can be won from the contract, but it’s unlikely. It’s possible that the system has bugs, or that the RandDAO oracle is attacked and the contract is drained, but this is not by design.
To avoid generating statistically burnt Ether, we could try to design the system to have a feature that allows us to drain these funds gracefully. However, every design has their own issues:
1) We could have a function that would slowly drain the contract of excess funds (we would just guess how much excess the contract probably has). This would put the contract in a bigger risk of being underwater
2) We require ALL the users to withdraw their funds at one point in time (turnstile) and then all the remaining funds are considered as profit. However, this is a big nuisance for users and would significantly harm the liquidity and anonymity pool. This could also be done in a rolling manner, but it would still have a negative impact on the protocol.
Anonymity pool is an important feature as it maintains the anonymity of all the users. The lottery enables us to have a unique way to do liquidity mining. We can increase the payout odds the longer time the user has kept their funds in the contract. While increasing the odds over the time leaks some statistical information about the deposits, the change could be made insignificant enough to be hidden inside the noise.
It’s also important that users maintain their anonymity after lottery participation. Tornado Cash notes contain information about the source of the funds. These notes should be thrown away after they are no longer needed. When a note is still required, it should be rerolled into a new note that no longer contains information about the source of funds. This is discussed more in detail in the previous blog post. These security precautions make it impossible to prove afterwards what happened in the contract even if private information gets leaked.
Casinos funds or users funds?
The contract treats the casino’s funds as the same as users’ funds. Essentially users depositing funds to the contract function as liquidity providers for the ZK-lottery. The winning prizes are paid partially from users’ funds and partially from the past casino profits. Interestingly, nobody knows the fraction of these. Statistically speaking, initially it’s all users’ funds and the longer the contract has been operating the bigger the fraction of casino’s own funds are used. Thanks to zero knowledge proofs, we can only try to guess the reality.
Curiously, we don’t even know if the lottery is being played or is everyone just setting the difficulty very low in order to return their funds with the highest certainty. Who knows. Does it even matter?