Ethereum’s New Privacy Layer Works Like zCash — But Without Leaving the Chain
May 4, 2025
I wrote a detailed article explaining how Tornado Cash works a few years ago. That project was my first real encounter with zero-knowledge-proof technology, and it helped me understand its inner workings. In this new article, I’ll introduce a concept that builds upon Tornado Cash — essentially a zCash-like “embedded blockchain.” It’s not a true Layer 2 solution, since all activity happens fully on-chain. But before we explore the mechanics, let’s take a moment to review the fundamentals.
How Does Tornado Cash Work?
Since I’ve already written a detailed article on the topic, I’ll give a summary here. If you’re interested in the technical deep dive, I recommend reading my previous articles:
In short, Tornado Cash works by having a user deposit ETH or another token into the Tornado Cash smart contract, along with a commitment. Each commitment has an associated nullifier, which is known only to the user. The smart contract stores the commitment in a Merkle tree. Later, the user can withdraw the funds from a different address by revealing the nullifier.
Because only the user knows the nullifier, no one can link the withdrawal to the original deposit. However, the smart contract still needs to verify that the nullifier is linked to a valid commitment. To prove this, the user submits a zero-knowledge proof that shows:
- The commitment is part of the Merkle tree (the user really deposited into the contract), and
- The nullifier was derived from that commitment.
Each nullifier can only be used once, ensuring that funds can’t be withdrawn more than once.
In Tornado Cash, the commitment and nullifier hashes are computed as follows:
commitment_hash = HASH(nullifier, secret) nullifier_hash = HASH(nullifier)
Since cryptographic hashes are one-way functions (you can’t determine the original input from the hash without brute force), it’s impossible to link the commitment_hash
and the nullifier_hash
just by looking at them. However, their relationship can be proven using zero-knowledge proof.
The main limitation of this system is that the amount transferred can leak information. For example, if you deposit 12.34 ETH and later withdraw exactly 12.34 ETH, someone could reasonably guess that the two are linked. That’s why Tornado Cash only supports fixed denominations like 0.001 ETH — if everyone uses the same amount, unlinkability is preserved.
The concept I’ll describe in the next section addresses this limitation, drawing inspiration from how zCash solves the problem.
Solving the Fixed Denomination Problem
The limitation of fixed deposit amounts can be overcome by introducing two additional methods alongside deposit and withdraw: split and merge.
The split function works similarly to a withdrawal, but instead of specifying an Ethereum address, the user provides two new commitments. A split allows the user to divide the stored amount into any two parts — and importantly, this happens without revealing the new amounts to anyone observing the blockchain. To enable this, the commitment must include the amount as part of its hashed data.
In the updated system, the commitment is structured as follows:
commitment_hash = HASH(amount, nullifier, secret)
This structure is essential because, during a split, the user must prove that the two new commitments contain amounts that add up exactly to the original commitment’s amount. It’s easy to see how this mechanism addresses the fixed denomination issue in Tornado Cash. For instance, a user could deposit an arbitrary amount like 100 ETH, split it into parts (e.g., 60 ETH and 40 ETH), and later withdraw them to different addresses. Since the values are hidden within commitments, no one can link the withdrawal to the original deposit based on the amount alone.
There’s also no need to immediately withdraw funds. Users can effectively store value in the smart contract itself, functioning as a kind of virtual wallet. This is enabled by the merge operation, which allows two stored commitments (referenced via their nullifiers) to be combined into a single new commitment.
To perform a merge, the user must prove that the total amount in the two input commitments matches the amount in the new commitment.
This gives rise to a kind of embedded ledger, where each user has a private balance and can send funds freely to others. Withdrawals are only necessary when someone wants to exit the system and spend their assets outside of this private layer.
Privacy-Preserving Layer on Ethereum
Let’s walk through an example to see how this privacy layer works in practice:
- Alice creates a 100 ETH pool on the smart contract by depositing 100 ETH along with a commitment.
- Bob does the same, creating a 10 ETH pool with his own commitment.
- Alice wants to send 10 ETH to Bob. She uses the split function, which consumes her nullifier and splits the original 100 ETH into two new commitments: one for 10 ETH and one for 90 ETH. Now, Alice holds two nullifiers — one representing 10 ETH, the other 90 ETH.
- Alice encrypts the 10 ETH nullifier using Bob’s public key and writes it to the blockchain.
- Bob decrypts the message using his private key and obtains the nullifier. He can now either withdraw the 10 ETH or merge it with his existing balance.
- Bob chooses to keep the funds in the system, so he uses the merge operation. He provides the nullifier from his own 10 ETH commitment and the one received from Alice, then creates a new commitment for 20 ETH.
After the transaction:
- Alice has a remaining balance of 90 ETH,
- Bob now controls 20 ETH in the privacy layer.
Because nullifiers and commitments carry no publicly visible information, and the messages between users are encrypted, all of these transactions happen completely anonymously.
Since users can freely transfer value within the system without revealing amounts or identities, there’s often no need to withdraw. The system behaves much like a Layer 2 blockchain, with one key difference: everything happens on-chain.
Conclusion
By introducing two simple operations — split and merge — we can significantly enhance the original Tornado Cash concept and transform it into a full-fledged privacy-preserving transaction layer on Ethereum. Unlike traditional mixers limited to fixed denominations, this upgraded model supports arbitrary amounts, enabling users to split, merge, and transfer value flexibly and anonymously.
At its core, the system relies on zero-knowledge proofs, commitments, and nullifiers to ensure unlinkability between deposits and withdrawals. By embedding the amount directly into the commitment, users can prove the correctness of value-preserving operations like splitting and merging, without ever revealing the actual amounts or participants involved.
The result is a kind of embedded blockchain that operates entirely on-chain, without the need for a separate Layer 2. Users can store funds privately, send assets to one another using encrypted messages, and only withdraw when they need to interact with the public Ethereum layer. This design combines the privacy features of zCash with the smart contract capabilities of Ethereum, creating a powerful framework for anonymous, programmable value transfer.
Best of all, this model is compatible with any EVM-based blockchain, making it a practical and scalable foundation for privacy-preserving applications in the broader Ethereum ecosystem.
Search
RECENT PRESS RELEASES
Related Post