当前位置:首页 行业动态 正文

一文读懂以太坊最新二层隐私技术Zkopru,实现ETH、ERC20和ERC721代币标准互通

2023-07-12

7月20日,Ethereum 9¾ 创始人 Wanseob Lim在太坊技术论坛 ethresear.ch上正式发布了Zkopru,这是一种二层(Layer 2)私人交易扩容解决方案,同时使用了zk-SNARK和Optimistic rollup技术。 它能够以很低的成本支持ETH,ERC20,ERC721代币在二层网络内进行私人转移和原子交换。 此外,借助预付款功能,用户可以在交易确认之前从二层提取资产。
非常高兴分享以太坊二层隐私技术Zkopru的实现。 从去年11月开始,我和@barryWhiteHat开始构建此项目,如今结果如下。

 

Zkopru是什么?

 
  • 它是一种二层(layer 2)私人交易解决方案。
  • 它使用了Optimistic rollup技术来管理区块。
  • 它使用了zk SNARK技术来创建私人交易

性能

 
  • 在以太坊网络上实现每笔私人交易成本仅8000gas。
  • 当gas限制为1195万且区块时间为13.2秒时,最大TPS为105

亮点功能

 
  • 支持ETH、ERC20、甚至ERC721
  • 支持私人原子互换。可以与私人订单匹配系统一起使用。
  • Subtree rollup将成本削减为merkle树形的20分之1
  • 在区块最终确认之前进行快速提款
  • 使用大量存款和大量迁移,我们可以构建一个第2层间网络。
而且,最重要的是,它现在已经开始生产区块! 欢迎在我们的测试网上试用: https://zkopru.network

 

介绍

 

Zkopru是一种二层(Layer 2)私人交易扩容解决方案,同时使用了zk-SNARK和Optimistic rollup技术。 它能够以很低的成本支持ETH,ERC20,ERC721代币在二层网络内进行私人转移和原子交换。 此外,借助预付款功能,用户可以在交易确认之前从二层提取资产。

交易

zk交易可以接受多个UTXO作为其流入(inflow),并为其流出(outflow)创建新的UTXO。 因此,最重要的是验证流入和流出。

7d555c11d3ac74c4a0db6106b16f805428556127_2_1050x1000

流入验证

Zkopru使用承诺消除器方案实现隐私。 这意味着zk交易花费了UTXO,而没有透露已使用了哪个票据。 取而代之的是,我们揭示了源自UTXO的无效符,但无法与其原始UTXO链接。

要使用UTXO,必须满足以下条件:

  • UTXO证明
tx构建器会提交每个UTXO的Merkle证明以证明其存在。 为了进行有效的SNARK计算,UTXO树使用Poseidon作为其哈希函数。
  • 所有权证明
只有所有者才可以使用UTXO。 在这种情况下,每个note都有一个公钥字段,即一个Babyjubjub点。 使用配对的私钥,所有者可以创建EdDSA签名以证明其所有权。
  • commitment证明
该回路应具有有关输入UTXO的详细信息,以计算流入的总和。 因此,所有者应提供详细信息,其Poseidon哈希值应等于Merkle证明和所有权证明的leef哈希值。
  • Nullifier证明
给定的Nullifier可以从输入的UTXO正确得出。

流出验证

zk交易可以创建三种类型的输出:UTXO,取款(Withdrawal)和迁移(Migration)。 如果zk事务创建了UTXO,则Zkopru将它们附加到UTXO树中。 创建提款输出时,Zkopru会将它们附加到提款树中。 最后,作为二层区块一部分的大规模迁移(mass migration),包括该区块每个zk交易的迁移输出。

因此,流出(outflow)应满足以下条件:

  1. 当输出为UTXO类型时,输出的公共哈希值等于在SNARK回路中计算出的值。
  2. 如果输出的类型是提款或迁移,则应显示详细信息,因为它将正确数量的资产移至网络外部。
零和证明(zero-sum proof)

最后,zk交易应保证流入等于流出,包括手续费。

 

区块结构

 

7ca5c3fb18baa85ea1c4b1f5d9042ba9207103e8_2_1128x1000

区块头(header)

数据的前372个字节应为区块头(header)。 区块头包含以下数据:

  WX20200720-172127@2x 区块体(block body)

区块主体由交易,大量存款和大规模迁移组成。 此外,区块头应包含来自区块体的正确信息。 如果区块头的值不正确,提出者将通过质询系统被削减。

交易

d592b30cb05b28ecf9628c96d0ff97aeab30ba3e_2_852x750

大量存款

fbab0542d90ce861f07565468aa53ddd5075f4c0

大规模迁移(mass migration)

787be20995fd6d83af417450d2907434d15e1ed5

账户

Zkopru将提出了一种新的公共密钥结构。 注意此规范未来将进行更新。

Zkopru帐户管理着1层和2层密钥对。 首先,该帐户具有一个以随机方式生成的私钥的以太坊帐户。 这用于1层上的交互。 其次,Zkopru钱包从以太坊帐户的私钥中创建一个Babyjubjub私钥和公钥集。 此Babyjubjub密钥集用于二层的EdDSA和加密的备注字段。

WX20200720-171800@2x

UTXO

很快会提出一种新的UTXO规范

WX20200720-171728@2x

Zkopru使用Poseidon哈希计算leaf哈希:

var intermediate_hash = poseidon(ether, pub_key.x, pub_key.y, salt)

var result_hash = poseidon(intemediate_hash, token_address, erc20, nft)

 

接收者如何知道?

 

zk交易可以为接收者包括81个字节的加密备注字段。 由于具有零知识特性,如果没有交互过程,接收者也无法知道怎么接收。 因此,为了保持非交互方式,我们可以在接受者的备注字段中放入一些加密的数据。

 

加密(encryption)

 

使用Diffie-Hellman密钥交换协议生成共享密钥。 为发送者生成共享密钥的详细步骤为:

1. 创建一个临时密钥及其同态隐藏值:

ephemeral=epublic_ephemeral=ge

2. 将其临时密钥乘以接收者的公共密钥:

recipient_pubkey=gashared_key=(ga)e

3. 准备对压缩数据进行加密:

data = {

salt // 16 byte

tokenId, // 1 byte

value, // 32 byte

}

4.使用chacha20算法对数据进行加密,并使用临时公共密钥创建备忘录数据:

ephemeral = random.new()

public_ephemeral = generator.multiply(ephemeral)

shared_key = recipient_jubjub.multiply(ephemeral)

ciphertext = chacha20.encrypt(data, shared_key)

memo = public_ephemeral + ciphertext

 

解密(decryption)

 

使用Diffie-Hellman密钥交换协议,接收者还使用公共临时密钥和私有密钥创建共享密钥。

1. 解析备注(memo)并获取共享密钥:

public_ephemeral, ciphertext = parse(memo)

shared_key = public_ephemeral.multiply(private_key)

2. 使用共享密钥解密ciphertext:

decrypted = ciphertext.decrypt(shared_key)

3. 接收者使用解密结果尝试生成各种可能的UTXO。 这是因为加密的数据只有49个字节以最小化调用数据(calldata)的大小。 因此,接收者应尝试各种组合以检查交易是否包括被恢复的UTXO哈希。 如果未能在TX中找到已恢复的UTXO,则认为TX没有接收方的输出。

5a2a3ae10211b52ae7344de4d9dc34336a87488a

压缩数据

 

为了最大程度地减少调用数据(calldata),Zkopru将原始数据压缩为49字节数据。 首先,它摆脱了加密候选者的公钥,因为接收者将使用自己的公钥来推断出。 并且,它使用Token ID,该Token ID将支持的token地址和索引从0映射到255。然后,由于value可以是ether,erc20Amount或nftId,因此接收者针对这3种情况创建了三种类型的UTXO。 最后,如果在交易的输出列表中存在任何推断出的UTXO,则接收者成功接收了UTXO。

局限性

Zkopru不会强制回路(circuit)检查加密协议。因此,如果发送者未使用适当的共享密钥或数据,则接受者将不会收到备注。

 

原子交换

 

Zkopru以直接方式支持原子交换。如果A和B想要交换其资产,则它们会彼此创建备注(note)并将所需的备注公开在交易数据上。然后,协调者应对相反的交易进行配对或被削减。

例如,爱丽丝(Alice)想用天的50ETH交换鲍勃(Bob)的1000 DAI。

  1. 爱丽丝支出她的60 ETH note,并为自己创建了10 ETH note,并为鲍勃创建了50 ETH note。
  2. 爱丽丝还计算她未来的1000 DAI note的哈希值,并将该哈希值暴露给她交易的swap字段。
  3. 鲍勃则支出他的3000 DAI note,并为自己创建了2000 DAI note,为爱丽丝创建了1000 DAI note。
  4. 鲍勃还计算他未来的50 ETH note的哈希值,并将该哈希值暴露给他的交易的swap字段。
  5. 一旦协调者匹配了交易池中成对的交易集,它将把交易对包括在一个新区块中。
  6. 如果一个区块仅包含其中一个,则协调者将被削减。
09dcea1b401e618fa321a78362c7105c48149620

Zkopru正在使用一种简单版本的原子互换。然而如果你想要检验一种基于MPC的zk原子互换模型,你可以在这里看到详细信息。

 

Merkle树结构

 

UTXO树和withdrawal树中的备注(note)在下一个版本将有64深度。将只有一个单一的UTXO树和一个提取树。

Zkopru的树林由UTXO树,nullifier树和withdrawal树组成。

UTXO树是仅追加用法,包含UTXO的Merkle树。 通过提交包含Merkle证明,用户可以将UTXO用作交易的流入。 并将交易的输出结果附加回最新的UTXO树中。

另外,如果zk-transaction创建withdrawal输出,则Zkopru会将它们附加到最新的withdrawal树中。 将树的根被标记为已完成后,所有者可以通过证明所有权来提取资产。

然后,通过 commitment-nullifier方案,将用过的UTXO的nullifier标记为在nullifier树(唯一的稀疏Merkle树)中使用。 如果交易试图使用已经无效的叶子(leaf),它将变为无效(nullified),并且挑战者系统会大幅削减区块提议者。

ca4e3fd77d2d108e0b1e79b8545489123e254a62

Merkle树规范

WX20200720-172743@2x

{% hint style=“warning” %}

UTXO树&withdrawal树在Burrito版本上有64深度 https://github.com/zkopru-network/zkopru/issues/35

{% endhint %}

 

如何管理UTXO树

 

单个UTXO树是用于成员资格证明的稀疏Merkle树。 它使用Poseidon哈希(SNARK中最便宜的哈希函数之一)生成zk SNARK证明以隐藏支出哈希及其路径。

要将新树叶(leaf)追加到UTXO树,协调者将执行以下步骤。 1.准备一个阵列。 2.协调者选择要包括的MassDeposits,并将MassDeposits中的每笔存款附加到阵列中。 3.二层交易生成新的UTXO。 将新生成的UTXO附加到阵列。 4.以区块大小为32对准备好的数组进行分割。5. 构造子树(sub-tree)并执行子树rollup。

假设UTXO树已被(2 ^ 31)个事项完全填充,系统将被填充的树进行存档并启动一个新树。 也允许使用归档树来引用交易的包含证明。

7fd022be927d62031a6aea5f3b6f514ed6b741d6

Nullifier树

 

每次转账,提款和迁移交易都支出带有包含证明的UTXO,并标记在nullifier树上使用的派生nullifier。因此,nullifier树是一个很大的稀疏Merkle树,它记录了深度为254的稀疏Merkle树中每一个用过的UTXO。因此,Zkopru使用最便宜的哈希函数keccak256作为nullifier树的哈希函数。

要更新nullifier树,协调者执行以下步骤:

  1. 选择交易(转移,取款,迁移)并从交易中收集所有nullifier。
  2. 检查是否存在任何已使用的nullifier。
  3. 将每个nullifier标记为已使用。在更新过程中,如果所有nullifier都没有更改nullifier树的根,请丢弃该交易,因为它会尝试进行双花。
就像UTXO树一样,Zkopru乐观地更新了nullifier树的根。如果有任何问题,我们可以通过生成防欺诈链上证明一个nullifier被使用了不止一次。要查看工作原理,请参阅 RollUpChallenge.sol和 SMT.sol。

 

提款树(withdrawal树)

 

与withdrawal树和UTXO树的唯一区别在于,withdrawal树使用keccak256作为哈希函数。 之所以使用keccak256,是因为Zkopru在智能合约上需要提取树的Merkle证明,而在SNARK回路中则需要UTXO树的Merkle证明。 在树的根定型后,withdrawal树中的叶子(leaves)在1层智能合约中便是可以提取的。

要更新提款树,协调者执行以下步骤:

  1. 收集已选择交易的所有withdrawal叶子。
  2. 拆分出区块大小为32的withdrawal数组。
  3. 构造子树(subtree)并执行子树rollup。
 

大量存款(mass deposit)

 

当用户将资产存入Zkropu时会发生什么:

  1. Zkopru合约将给定数量的资产从用户帐户转移到自身。
  2. 验证note是否带有给定信息的一个有效哈希。
  3. 将note合并到MassDeposit []列表的最后一项。
什么是MassDeposit?

MassDeposit是用于rollup证明的单个mergedLeaves bytes32值。 可在此处检查什么是rollup证明mergedLeaves。 如果协调员提出一个包含MassDeposits的区块,则该区块会将MassDeposit中的所有note追加到其UTXO Merkle树。

协调员如何处理MassDeposits?

协调员只能包括不再更改的“已承诺(commited)” MassDeposit。 为了包含MassDeposit,协调员将监视Zkopru合约中的存款事件。

MassDeposit什么时候变成“已承诺”?

尽快将存款推到二层网络。 因此,当协调员提出每个新区块时,它将冻结最新的MassDeposit。

协调员可以包含多个MassDeposit吗?

是的,可以在最大挑战成本范围内一次包含多个MassDeposit。

 

大规模迁移(mass migration)

 

大规模迁移的基本思想非常简单。 虽然1层合约上的存款交易创建了MassDeposit对象,但是交易的“迁移”类型输出可以创建MassMigration,该MassMigration为其目的地网络构造MassDeposit。

交易可以具有UTXO,迁移(migration)或取款(withdrawal)类型的输出。

在Zkopru中,要进行迁移,就会涉及到源网络和目标网络。 一旦完成源网络上的大规模迁移(相关代码请查看),就可以执行源网络上的migrationTo函数。 该函数可以移动资产(包括Ether,ERC20和ERC721),同时为目标网络创建MassDeposit对象。

因此,目标网络应实现acceptMigration函数。 更多信息在这里

rollup之间的迁移标准将通过EIP进行标准化。

 

即时取款(instant withdrawal)

 

在Zkopru中,提取者可以通过设置每个提取note的即时提取费用来请求即时提取。 然后,任何人都可以提前为未完成的提款付款并收取费用。

为了请求即时提款,所有者为她的note生成ECDSA签名并进行广播。 拥有足够资产且可支付的任何人都可以使用签名提前支付取款。 一旦Zkopru成功包含该交易,智能合约便将提款note的所有权转移给付款人。 最后,预付款人在完成交易后将其提取。

我们可以有一个分散的公开市场来收取即时取款费。 要跟进最新进展,请订阅此github: https://github.com/zkopru-network/zkopru/issues/333

结论

 

根据此规范,我们已成功使用Circom,Solidity,Typescript等构建了测试网。

使用zk-SNARK和Optimistic rollup的以太坊二层私人交易扩展解决方案。 -zkopru-network / zkopru

首先,我们可以实现了一种每笔zk交易可承受的gas成本。 平均值约为8800gas,当gas限制为1,150,000且区块时间为13.2秒时,理论上最大的TPS为105。 在Zkopru中,交易数据消耗约534个字节。 由于证明数据为256字节,因此如果将来应用证明聚合,我们可以减少大约两倍的交易成本。 否则,每个区块提出和最终确定的存储成本分别约为168k gas和55k gas。 当我们包括350笔交易时,此成本约为区块生成成本的6.7%。

此外,我们可以利用Optimistic Rollup的灵活性来实现许多功能。 首先,Zkopru通过多个SNARK验证密钥支持各种类型的交易。 您甚至可以使用1个输入和4个输出,或4个输入和1个输出完成一笔交易。 通过Optimistic Rollup的灵活性,使其支持多种类型的交易非常简单。 其次,Zkopru实现了精确类型的挑战案例。 这意味着,如果区块的第n个交易有问题,则质询仅检查该特定交易。

另外,Zkopru需要您在计算机上运行节点,这一点也很重要。 因此,SNARK高效率性和轻节点是软件实现要考虑的重要因素。 因此,我们将使用Typescript和NodeJS构建该项目,以供将来在基于本机的移动应用程序中使用。 预计轻型节点将仅消耗约50〜100 MB的存储空间用于树管理。

总结一下工作,我们希望Zkopru可以用于以太坊的隐私交易层。 它既快速,便宜,又可移植到升级版本。 欢迎感兴趣的人为该项目进行捐款。 您可以通过Zkopru的文档页面(https://docs.zkopru.network)查看一个已经组织过的版本。

感谢你阅读本文。

参考文献:

  1. Ethereum 9 3/4: Optimistic rollup for zk-Mimblewimble1
  2. BarryWhitehat’s zk-rollup1
  3. John Adler’s Minimal Viable Merged Consensus1
  4. Plasma-group’s Optimistic Rollup
  5. Batch Deposits for [op/zk] rollup / mixers / MACI
  6. Mass migration to prevent user lockin in rollup