Explain the design principle of Tornado Cash V2 in detail

The developer ameen.eth combines the concept of "Proof-of-Innocence" with Tornado Cash to provide another direction that "privacy is not equal to crime".

Written by: Albert Lin

Preface

TornadoCash is a well-known anonymous transaction service in the cryptocurrency world. TornadoCash uses ZKP (Zero-Knowledge Proof) technology to hide the source of funds. The U.S. government argued that such a mechanism facilitated illicit financial flow activities, and was eventually sanctioned by the U.S. Treasury Department in August 2022 and forced to take it off the shelves. Privacy protection and money laundering always seem to go hand in hand in many cases. While pursuing privacy, criminals often use these privacy features to launder illegal funds. Can a way be found that allows people to have privacy while effectively curbing money laundering? Privacy-Pools by ameen.eth, an early developer of TornadoCash, may give a direction. (Only the front-end website and GitHub Repository are affected for the delisting part, and the contract part is not affected because it is on the blockchain. Finally, GitHub restored the Repository under the efforts of the Electronic Frontier Foundation. For details, please refer to here)

Introduction TornadoCash principle

Before introducing Privacy-Pools, you need to understand the design principles related to TornadoCash. For a detailed introduction, please refer to my previous article "Breaking Down TornadoCash: A Beginner's Guide to Explaining its Functionality to Friends". Here is a brief review of the design principles of TornadoCash.

TornadoCash uses receipts (commitments) to control access. The receipt is generated by hashing the secret (secret value) and nullifier (logout code), and each receipt can only be withdrawn once. Use Merkle Tree (hash tree) to record deposit information, use receipt as leaf node and calculate Merkle Root (hash tree root). The user only needs to provide the data from the leaf to the root to prove whether the data is one of the leaves of the Merkle Tree, and indirectly prove that there was deposit funds to TornadoCash before. Use Zero-Knowledge Proof to hide deposit source, and use nullifier to prevent Double Withdrawal attack.

TornadoCash's receipt has two meanings

  • Proof that the sender has deposited funds before
  • Make sure each recipient can only claim funds once

「Proof-of-Innocence」

According to the design principle of TornadoCash, it can only be known that the funds received must come from a previous deposit, but it is not known which deposit it came from. This is the main purpose of criminals using TornadoCash for illegal money laundering activities, and it is also the reason why the US government regulates TornadoCash. If we can propose another Proof (ZKP) to prove that the funds received are not from the deposits on the rejection list, whether it can prove that the payment is not from criminals, this is "Proof-of-Innocence" (" Proof of innocence") core concept.

The concept of "Proof-of-Innocence" can be divided into two directions

  • Evidence that the funds received are from the collection of funds deposited in the allowed list (allowed list)
  • Evidence that the funds being withdrawn did not come from the set of deposit funds on the reject list (reject list)

Both of these methods can prove that the withdrawal funds are not from the deposits in the rejection list. The approach in the figure below is to use the rejection list to prove that the funds received do not come from the deposit (red) in the rejection list.

source:

Privacy-Pools design principle

Privacy-Pools adds the concept of "Proof-of-Innocence" to TornadoCash. In addition to the original meaning of the TorandoCash receipt, the Privacy-Pools payment receipt has a third meaning: "to prove that the funds received come from the deposits in the allowed list".

Privacy-Pools receipt means:

  • Proof that the sender has deposited funds before
  • Make sure each recipient can only claim funds once
  • Evidence that the funds to be withdrawn come from deposits in the allowed list

Here we use Allow Merkle Tree to explain how Privacy-Pools uses "Proof-of-Innocence" in this system (Allow Merkle Tree is the concept of using the permission list). First of all, Allow Merkle Tree has several characteristics

  • Allow Merkle Tree as the name suggests is a Merkle Tree
  • The tree height and number of nodes are the same as the Deposit Merkle Tree of Privacy-Pools. *All the leaves of the Allow Merkle Tree correspond to the Deposit Merkle Tree leaf (deposit) position.

Allow Merkle Tree leaf data can be divided into the following two categories:

  • allowed: Indicates that the deposit at this location is allowed. (Positions that have not yet been deposited are also allowed by default)
  • blocked: Indicates that the deposit at this location is rejected.

As shown in the figure below, the positions of index 0 and 1 are both legal funds in the Deposit Merkle Tree, and the corresponding positions of the Allow Merkle Tree leaf are also allowed.

Assuming that today a criminal wants to carry out money laundering activities, he deposits the illegal funds obtained after the attack into Privacy-Pools, and the deposit location is Deposit Merkle Tree index: 2. We know that it is illegal funds, so we update it to blocked at the corresponding Allow Merkle Tree index: 2 position.

Permission List Collection Situation

Assuming that today, a user who deposits in the US government's allowed list wants to withdraw funds, he needs to provide "proof that the deposit is in the Dposit Merkle Tree" and "proof that the one in the US allowed list is Allowed". The proof corresponding to the US allow list includes the Allow Merkle Root (provided by the user, subetRoot in the code) and the node value that will be passed on the way. When Privacy-Pools verifies the ZKP stage, it will use the leaf value as allowed (keccak256(allowed) in the actual code) and the given node value to pass through to construct a Merkle Root. Verify that this Merkle Root is the same as the Allow Merkle Root provided by the user. If the verification of the same representative is passed, it means that the fund for the withdrawal is from a deposit that exists in the US government's permission list.

Rejection List Collection Situation

Today, a user who is not on the US government's allowed list wants to withdraw funds, and the corresponding deposit location is marked as blocked in the US government's allowed list. This will cause the user to be unable to use the Allow Merkle Root of the US government's permission list to withdraw funds, because the corresponding proof cannot be generated and the verification will fail (because Privacy-Pools uses the value of allowed for leaf to do calculations, and the US government's permission list will be The location is marked as blocked, so the same Merkle Root cannot be calculated).

Such a design forces the user to provide another Allow Merkle Root to withdraw funds (other Allow Merkle Trees need to mark the deposit location as allowed in order to calculate the same Allow Merkle Root to pass the verification). This other Allow Merkle Tree may be maintained by other governments or institutions, or even generated by this user himself. Today, the U.S. government can use the Allow Merkle Root used when withdrawing to determine whether the user's funds comply with the laws and regulations of the U.S. government, so as to achieve the purpose of tracking. If the user is using the Allow Merkle Tree generated by himself or not credible, basically the withdrawal fund is very likely to come from a problematic deposit (every credible third-party Allow Merkle Tree marks the deposit location as blocked) .

Frequently Asked Questions

**Q: If the Allowroot is provided by the withdrawer, is it possible to forge a fake Allow Merkle Root to prove that the leaf is a deposit in the allowed list, does it mean that the money can still be taken away? **

A: The answer is yes, it is indeed possible to take the money away. The author specifically pointed out that such a mechanism is not to prohibit criminals from taking the money away, but even if they can take it away, it will be known that the funds are funds on the rejection list. When the withdrawer provides an unconvincing Allow Merkle Root, it can basically be regarded as withdrawing from the rejection list. The author guesses that the reason for allowing this is to maintain the decentralized nature of this service. Because each Allow Merkle Tree needs certain authority management to update the status of each leaf. If a certain allow tree root is mandatory, it means that someone has certain authority to control the withdrawal of funds, which is not in line with the spirit of decentralization.

**Q: Who will decide whether the transaction is from reject list funds? **

A: The part I saw was not specifically mentioned, and my understanding is that this part should be done by each regulatory agency. Assuming that today the U.S. government wants to check Privacy-Pools’ dirty money, he can check whether he is dirty money by checking the Allow Merkle Root of each transaction. As for what kind of Allow Merkle Root is allowed, that is for each regulatory agency to judge for themselves.

Privacy-Pools code

The main code and the author's own comments are attached here, hoping to help everyone understand the main logic through the code.

// circuits/withdraw_from_subset.circom

template WithdrawFromSubset(levels, expectedValue) {

// public

signal input root;

signal input subsetRoot;

signal input nullifier;

signal input assetMetadata; // abi.encode(token, amount).snarkHash();

signal input withdrawMetadata; // abi.encode(recipient, refund, relayer, fee).snarkHash();

// private

signal input secret;

signal input path; // Indicate whether the data represents the left leaf or the right leaf.

signal input mainProof [levels] ; // Construct the data required for deposit root.

signal input subsetProof [levels] ; // Construct the data required for allow root.

// Calculate the nullifier and commitment.

component hasher = CommitmentNullifierHasher();

hasher.secret <== secret;

hash.path <== path;

hasher.assetMetadata <== assetMetadata;

nullifier === hasher.nullifier;

// expectedValue: keccak256("allowed") % p

component doubleTree = DoubleMerkleProof(levels, expectedValue);

doubleTree.leaf <== hasher.commitment;

// Convert the path to bits to specify whether it is the left leaf or the right leaf.

// It can be observed that the deposit tree and allow tree share the same path.

doubleTree.path <== path;

for ( i = 0; i < levels; i++) {

doubleTree.mainProof [i] <== mainProof [i] ;

doubleTree.subsetProof [i] <== subsetProof [i] ;

}

root === doubleTree.root; // Verify the deposit root.

subsetRoot === doubleTree.subsetRoot; // Verify the allow root.

signal withdrawMetadataSquare;

withdrawMetadataSquare <== withdrawMetadata * withdrawMetadata;

}

TLDR

  • "Proof-of-Innocence" is to use another Proof to prove that the payment comes from the deposit in the allowed list. "Proof-of-Innocence" can be constructed from two perspectives: allow list and deny list.
  • Privacy-Pools superimposes the concept of "Proof-of-Innocence" on the basis of TornadoCash. The original receipt represents a third meaning: "Proof that the funds received come from the deposits in the allowed list."
  • The existence of the Allow Merkle Tree can prove that the withdrawal of funds exists in the allowed list. The leaf position of the Allow Merkle Tree corresponds to the deposit leaf position of the Deposit Merkle Tree. Allow Merkle Tree leaf data is allowed and blocked.
  • In addition to the information required to construct the Deposit Merkle Root, the recipient also needs to provide the Allow Merkle Root and the construction Allow Merkle Root information to prove that the withdrawal of funds exists in the allowed list.
  • Since the Allow Merkle Root is provided by the recipient, criminals still have a way to take away the illegal funds through the fake Allow Merkle Root. The fake Allow Merkle Root will still appear on the chain and be regarded by others as doubtful about the payment, so as to track the flow of illegal funds.

The developer ameen.eth combines the concept of "Proof-of-Innocence" with TornadoCash to provide another direction that "privacy is not equal to crime". The author finds it interesting to use another ZKP to prove another fact, which is a bit like the addition of ZKP. This way of use will be simpler and more efficient than constructing a larger and more complex ZKP. Regarding the choice of Allow Merkle Tree, I feel that it will be constructed by a fairer unit in the future, which will be more persuasive to others.

Finally, thanks to Chih-Cheng Liang and Ping Chen for helping to review the article and giving valuable opinions!

Reference:

Stealth Address

Tornado Cash instance analysis

Introduction to the development of ZKP and smart contracts

[ZKP Reading Club] TornadoCash

Tornado Cash — How it Works | DeFi + Zero Knowledge Proof

In-depth understanding of TornadoCash technical details

0xhhh Thread introducing new features and their principles

demo video

Vitalik's talk on how to improve Tornadov2

privacy pools v0tweet

Introducing Proof of Innocence built on TornadoCash

View Original
The content is for reference only, not a solicitation or offer. No investment, tax, or legal advice provided. See Disclaimer for more risks disclosure.
  • Reward
  • Comment
  • Share
Comment
0/400
No comments
Trade Crypto Anywhere Anytime
qrCode
Scan to download Gate app
Community
English
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)