Tornado Cash 本质上是一个基于零知识证明的资金混合池,核心思路可以用一句话概括:
把很多人的钱放进同一个"固定面额"的池子里搅混,取款时只需证明"我确实存过钱",而不用暴露"我存的是哪一笔"。
存款
-
本地生成一个随机秘密 secret 与 nullifier,算出它的哈希 commitment,这有哈希会公开上链
commitment = Hash(nullifier, secret) -
同时算出 nullifierHash 留着取款的时候用,要公开的值(唯一目的是防双花,合约会检查它有没有被用过)
nullifierHash = Hash(nullifier), -
调用合约 Deposit 存入固定金额(比如 1 ETH),把 commitment 记录到链上的 Merkle 树里
rootnew = H(…H(H(leafi,siblingi),⋅)…) -
此时链上记录
{存款地址addr, commitment, v=1ETH, 时间戳}
取款
-
重新构建 Commintment 和 NullifierHash
-
确定 Commitment 是在 Merkle 树的第几个叶子节点
-
确定 Recepient (以及可选的 relayer、fee)
-
构造 ZK Proof
π = Prove( 私密输入: nullifier, secret, path_i ; 公开输入: root, nullifierHash, recipient, relayer, fee ) -
提交取款
withdraw(π, root, nullifierHash, recipient, relayer, fee) -
合约验证逻辑
a. 验证 root 是历史上出现过的合法根
b. 验证 nullifierHash 没有被用过(防双花核心)
c. 验证 zk-proof 合法性
-
为什么是固定额度?如果转账不同的额度怎么处理?
保护金额的匿名性(避免通过金额把存款和取款关联起来)
如果想转不同的额度,只能选择最接近的固定面额,多次使用不同池子(0.1 / 1 / 10 / 100 ETH)
-
匿名集的大小影响到实际隐私度
如果在某个时间段内,只有一笔存款,关联性极大,任然可能被追踪。
-
前端被篡改风险
如果前端被插入恶意代码,可能把用户的 deposit note 发给攻击者