How does it work?
To achieve privacy, Tornado Cash uses smart contracts that accept token deposits from one address and enable their withdrawal from a different address. Those smart contracts work as pools that combine all deposited assets.
Once the funds are withdrawn by a new address from those pools, the on-chain link between the source and the destination addresses is broken through anonymity. While assets are in anonymity pool the management of are non-custodial, individuals are the only ones in the control of their assets given that sufficient operational security is maintained.
Classic anonymity pools
- When a user transfers assets into a pool (deposit), a private note is generated. This private note works as a private key or secret to access the assets
Nova anonymity pools:
- Asset management is fulfilled through a shielded key, which can be generated signing a message with an Ethereum address for reusable access
- Custody is obtained by either transferring assets to the pool or registering a shielded key to receive shielded transfers
The strength of such protocol is linked directly to its amount of users and the size of its pool. The more users deposit into the pool the larger the probability of correlation. However, to ensure anonymity individuals must be conscious of:
- Using a relayer for withdrawal gas fees and maintain a shielded balance
- Waiting for sufficient subsequent deposits for decreased chances of probability or relation to the withdrawal
For a more detailed explanation see guide Tips to remain anonymous.
Zero knowledge
Tornado Cash uses Zero-Knowledge Succinct Non-Interactive Argument of Knowledge (zkSNARK) to maintain non-custodial shielded transactions.
To process a deposit a random slice of bytes is generated by the individual, it is then encrypted using Pedersen hashing. Pedersen hashing is optimized for the arithmetic circuits for zero-knowledge proofs and maintain low transactional costs in the Ethereum virtual machine. Depositing is fulfilled through inputing the hash for insertion into the Merkle tree.
To process a withdrawal, the same area of bytes is split into two separate parts: the secret on one side & the nullifier on the other side. The nullifier is hashed. This nullifier is a public input that is sent on-chain to be checked with the smart contract & the Merkle tree data. It avoids double-spending for instance.
Thanks to zk-SNARK, it is possible to prove the 20 MiMC hash of the initial commitment and of the nullifier without revealing any information. Even if the nullifier is public, privacy is sustained as there is no way to link the hashed nullifier to the initial commitment. Besides, even if the information about the transaction is present in the Merkle root, the information about the exact Merkle path, and subsequently the location of the transaction, is still kept private.
Deposits are simple from a technical point of view, but expensive in terms of gas as they need to compute the 20 MiMC hash & update the Merkle tree. On the other hand, the withdrawal process is complex but cheaper as gas is only needed for the nullifier hash and the zero-knowledge proof.