EIP-7503: 零知识虫洞
| Authors |
|---|
摘要
在研究隐私解决方案和 ZKP 的应用时,我们发现了一种技术,通过该技术,人们可以通过将他们的数字资产(例如 ETH)发送到一个不可花费的地址来销毁它,然后构建一个 ZK 证明,表明一些代币存在于一个不可花费的账户中,而不泄露该账户。
该 EIP 提议向以太坊添加一个铸造功能,以便人们可以重新铸造他们有目的地销毁的以太币。所提及的隐私解决方案将为发送者带来强大的貌似可信的否认级别,因为没有办法证明发送者一直在参与隐私协议。 这也将创建一个匿名池,默认情况下包括所有没有传出交易的以太坊账户。
规范
参数
MAGIC_ADDRESS:0xfe(一个字节)MAGIC_NULLIFIER:0x01(一个字节)MAGIC_POW:0x02(一个字节)MAGIC_CHANGE:0x0404040404040404040404040404040404040404040404040404040404040404POW_LOG_DIFFICULTY:24MAX_DEPOSIT:32 10*18weiWormholeTxType:TBDWORMHOLE_NULLIFIER_ADDRESS:TBDRECEIPT_PREFIX:TBD(数据类型List[bool])
- - -
我们定义了一个新的 EIP-2718 交易类型,其中 TransactionType 是 WormholeTxType 并且 TransactionPayload 格式如下:
[chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, data, access_list, root_beacon_block_number, proof]
验证这种类型的交易需要确认:
- 该证明是一个零知识证明:
- 私有输入:
secret,main_index,main_branch,privacy_pool_index,privacy_pool_branch,deposit_value,deposit_sender,change_value_salt,change_value - 公共输入:
beacon_block_root,nullifier,privacy_pool_root,withdraw_value,change_value_hash - 函数:验证以下所有条件
verify_merkle_branch(root=beacon_block_root, index=RECEIPT_PREFIX + main_index, leaf=make_receipt_hash(deposit_sender, deposit_value, change_value_salt, secret), branch=main_branch)verify_merkle_branch(root=privacy_pool_root, index=privacy_pool_index, leaf=make_receipt_hash(deposit_sender, deposit_value, change_value_salt, secret), branch=privacy_pool_branch)nullifier == sha256(MAGIC_NULLIFIER + secret)sha256(MAGIC_POW + secret) % 2**POW_LOG_DIFFICULTY == 0change_value_hash == sha256(change_value_salt, change_value)change_value + withdraw_value == deposit_value(所有值都是无符号整数,还需要确认没有溢出)deposit_value <= MAX_DEPOSIT
SLOAD(WORMHOLE_NULLIFIER_ADDRESS, proof.nullifier) == 0get_beacon_block_root(root_beacon_block_number) == proof.beacon_block_root
我们将 make_receipt_hash 定义如下:
def make_receipt_hash(deposit_sender: Address, value: uint256, change_value_salt: bytes32, secret: bytes32):
if deposit_sender != 0:
to_address = sha256(MAGIC_ADDRESS + secret)[12:]
topics = [MAGIC_EIP_7708, deposit_sender, to_address]
logdata = int_to_bytes32(value)
else:
topics = [MAGIC_CHANGE, hash(change_value_salt, value)]
logdata = bytes([])
return get_ssz_root_hash(Log(topics=topics, data=logdata))交易的哈希值(不包括证明)应包含在生成零知识证明的随机性中。这确保了证明充当签名。
如果交易得到确认,则在地址 WORMHOLE_NULLIFIER_ADDRESS 生成 proof.withdraw_value 以太币。然后使用该值从此地址继续进行常规调用。在调用之前,设置 SSTORE(WORMHOLE_NULLIFIER_ADDRESS, proof.nullifier, 1),并发送一个日志事件,其中 topics = [MAGIC_CHANGE, proof.change_value_hash] 并且 logdata = bytes([])。
理由
在基于椭圆曲线的数字签名中,通常存在一个秘密标量 $s$,从中计算出一个公钥(通过将生成器点与标量相乘:$s \times G$)。 以太坊 EOA 地址是公钥的 keccak 哈希。
此外,如果智能合约参数的 keccak 哈希等于该地址,则智能合约可以花费以太坊地址中的资金。
因此,一个以太坊地址 $A$ 可以花费当且仅当:
- 存在一个私钥 $s$。 使得 $A = keccak(s \times G)$。
- 存在一个智能合约 $c$,使得 $A = keccak(c_{params})$。
哈希函数的前像抵抗属性意味着,你不能找到 $x$ 使得 $keccak(x)=r$,如果 $r$ 是一个随机值。 因此,发送到一个随机以太坊地址 $r$ 的资金是不可花费的,但是其他人如何确定 $r$ 确实是随机的,而不是计算 $s \times G$ 的结果?
一个很好的随机性来源是哈希函数。 如果地址等于秘密前像 $s$ 的哈希,我们可以得出结论,该地址是不可花费的,因为不存在多项式有界算法来找到 $x$ 使得 $keccak(x)=h(s)$。 只有当第二个哈希函数是一个不同的哈希函数时,这才是真的,并且它假设不可能找到 $x_1$ 和 $x_2$ 使得 $h_1(x_1)=h_2(x_2)$,如果 $h_1$ 和 $h_2$ 是不同的哈希函数。
借助零知识证明,我们可以隐藏 $s$ 的值! 我们只需要证明我们知道一个秘密值 $s$,其中地址是 $h(s)$。 我们可以更进一步。 我们可以证明在状态根中存在一个以太坊账户,该账户持有一定数量的 ETH 并且不可花费。
通过将其透露给以太坊区块链并提供类似 nullifier 的东西(例如 $h(s | 123)$,以便不可能双重铸造相同的销毁代币),我们可以为 ETH 添加一个新的铸造功能,以便人们可以将他们秘密销毁的代币迁移到一个全新的地址,而无需在区块链上有任何痕迹。
像 TornadoCash 这样的加密货币混合器可以成功地混淆以太坊交易,但政府很容易禁止它们的使用。 任何与混合器合约有互动的人,无论是发送者还是接收者,都可以被标记。 然而,这个 EIP 试图通过要求零智能合约交互才能发送资金来最小化发送者的隐私泄漏,因此我们只使用普通的 EOA 到 EOA 的转移。 为了拥有一个“传送”机制,我们将所有 Secp256k1 点 $E(K)$ 的集合划分为两个子集/地址空间:
- 可花费地址空间:$\\{p \in \\{0,1\\}^{160} | \exists s : keccak(s \times G)=p \lor \exists c : keccak(c_{params})=p \\}$
- 不可花费地址空间:$\\{p \in \\{0,1\\}^{160} | \nexists s : keccak(s \times G)=p \land \nexists c : keccak(c_{params})=p \\}$
可花费/不可花费的地址是无法区分的,因此我们可以利用这个事实,并为发送到无法使用常规椭圆曲线签名花费的地址的资金定义一个可花费性规则。
存款是通过将以太币发送到地址 sha256(MAGIC_ADDRESS + secret)[12:] 来进行的,在这里你知道 secret。 这确保了除非你透露,否则没有人知道存款:链上的存款与向朋友发送以太币是无法区分的。 取款使用零知识证明来证明先前存款的 secret 的知识。
存款可以来自两个地方:通过发送以太币进行的标准存款,或零钱存款。 当从另一个存款(标准或零钱)中提取资金时,会自动生成零钱存款。 不需要提取整个存款金额; 例如,如果存入了 20 个以太币并且只提取了 12 个,系统将创建一个新的 8 个以太币的零钱存款。 此零钱存款的值被哈希,因此查看链的人无法计算 零钱存款值 + 提款值 = 旧存款值 并使用该值来暴露原始存款。
此 EIP 要求 EIP-7708 以及一个将收据更改为 SSZ 格式的 EIP。 这简化了证明,因为它只需要验证一个验证日志的常规 SHA256 Merkle 分支,不需要在证明中执行 verkle 或 RLP 或其他复杂的逻辑。 该日志可以有两种类型:EIP-7708 ETH传输日志,或此 EIP 通过零钱机制本身生成的日志。
该 EIP 还包括一个隐私池[^1]机制,你必须提供一个隐私池根,该根对应于存款列表,因此你证明你的存款是该列表中的存款之一。 由于这种第二种 Merkle 分支机制已集成到证明中,因此用户使用它的边际成本为零,从而确保了最大程度的采用并最大限度地提高了负责任地使用隐私的机会。 隐私池论文更详细地解释了这个隐私池机制的价值,以及几乎所有合法用户都使用它的生态系统的好处。
内置的 PoW 和 32 ETH 限制可以防止哈希冲突攻击:如果攻击者可以找到冲突 (x1, x2) 使得 create2_address(..., x1) == sha256(MAGIC_ADDRESS + x2),攻击者可以提取两次他们的以太币(但只能提取两次)。 这种攻击需要大约 280 个哈希。 添加 24 位的 PoW 将难度增加到 292。 可以计算出这么多哈希,但这非常困难; 目前,挖掘一个比特币区块需要大约 2**79 个哈希,因此那些有能力攻击此机制的人在比特币挖掘中有一个更有利可图的机会。
可扩展性含义
如果电路能够同时在单个证明中重新铸造多个销毁的总和,那么商家和 CEX 将能够接受他们的销毁地址付款,并通过在区块链上存储单个证明(和一堆 nullifier)将他们的资金累积到一个地址,这将大大减少区块链上的交易数量。 将使用此 EIP 作为可扩展性解决方案的人员也将增加协议的隐私保证。
向后兼容性
使用 mint 函数生成的以太币与原始以太币不应有任何差异。 人们应该能够使用那些铸造的以太币来支付 gas 费。
参考实现
待确定 (TBD)。
ZK-SNARK 实现
同样 TBD,但该实现必须满足以下条件:
- 使用透明设置; 理想情况下,重用 EIP-4844 设置。 不要使用 Groth16 或其他特定于应用程序的设置。
- 该电路应该可以直接验证,而无需盲目信任像 Circom 这样的编译器。 SHA256 并不太复杂,可以使用 PLOOKUP 和一个 8 位查找表来实现算术运算,这应该足够了。 没有必要过度关注证明者的效率。
- 仍然必须使用椭圆曲线; Stark 太大了。
安全注意事项
如果此 EIP 的实现出现错误,人们可能会铸造无限量的 ETH,从而导致以太坊价格崩溃。
版权
在 CC0 下放弃版权和相关权利。
[^1]:
{
"type": "article",
"id": 1,
"author": [
{
"family": "Buterin",
"given": "Vitalik"
},
{
"family": "Illum",
"given": "Jacob"
},
{
"family": "Nadler",
"given": "Matthias"
},
{
"family": "Schär",
"given": "Fabian"
},
{
"family": "Soleimani",
"given": "Ameen"
}
],
"DOI": "10.2139/ssrn.4563364",
"title": "Blockchain Privacy and Regulatory Compliance: Towards a Practical Equilibrium",
"original-date": {
"date-parts": [
[2023, 9, 6]
]
},
"URL": "https://ssrn.com/abstract=4563364",
"custom": {
"additional-urls": [
"http://dx.doi.org/10.2139/ssrn.4563364"
]
}
}def make_receipt_hash(deposit_sender: Address, value: uint256, change_value_salt: bytes32, secret: bytes32): if deposit_sender != 0: to_address = sha256(MAGIC_ADDRESS + secret)[12:] topics = [MAGIC_EIP_7708, deposit_sender, to_address] logdata = int_to_bytes32(value) else: topics = [MAGIC_CHANGE, hash(change_value_salt, value)] logdata = bytes([]) return get_ssz_root_hash(Log(topics=topics, data=logdata)) ```
交易的哈希值(不包括证明)应包含在生成零知识证明的随机性中。这确保了证明充当签名。
如果交易得到确认,则在地址 WORMHOLE_NULLIFIER_ADDRESS 生成 proof.withdraw_value 以太币。然后使用该值从此地址继续进行常规调用。在调用之前,设置 SSTORE(WORMHOLE_NULLIFIER_ADDRESS, proof.nullifier, 1),并发送一个日志事件,其中 topics = [MAGIC_CHANGE, proof.change_value_hash] 并且 logdata = bytes([])。
理由
在基于椭圆曲线的数字签名中,通常存在一个秘密标量 $s$,从中计算出一个公钥(通过将生成器点与标量相乘:$s \times G$)。 以太坊 EOA 地址是公钥的 keccak 哈希。
此外,如果智能合约参数的 keccak 哈希等于该地址,则智能合约可以花费以太坊地址中的资金。
因此,一个以太坊地址 $A$ 可以花费当且仅当:
- 存在一个私钥 $s$。 使得 $A = keccak(s \times G)$。
- 存在一个智能合约 $c$,使得 $A = keccak(c_{params})$。
哈希函数的前像抵抗属性意味着,你不能找到 $x$ 使得 $keccak(x)=r$,如果 $r$ 是一个随机值。 因此,发送到一个随机以太坊地址 $r$ 的资金是不可花费的,但是其他人如何确定 $r$ 确实是随机的,而不是计算 $s \times G$ 的结果?
一个很好的随机性来源是哈希函数。 如果地址等于秘密前像 $s$ 的哈希,我们可以得出结论,该地址是不可花费的,因为不存在多项式有界算法来找到 $x$ 使得 $keccak(x)=h(s)$。 只有当第二个哈希函数是一个不同的哈希函数时,这才是真的,并且它假设不可能找到 $x_1$ 和 $x_2$ 使得 $h_1(x_1)=h_2(x_2)$,如果 $h_1$ 和 $h_2$ 是不同的哈希函数。
借助零知识证明,我们可以隐藏 $s$ 的值! 我们只需要证明我们知道一个秘密值 $s$,其中地址是 $h(s)$。 我们可以更进一步。 我们可以证明在状态根中存在一个以太坊账户,该账户持有一定数量的 ETH 并且不可花费。
| 通过将其透露给以太坊区块链并提供类似 nullifier 的东西(例如 $h(s | 123)$,以便不可能双重铸造相同的销毁代币),我们可以为 ETH 添加一个新的铸造功能,以便人们可以将他们秘密销毁的代币迁移到一个全新的地址,而无需在区块链上有任何痕迹。 |
像 TornadoCash 这样的加密货币混合器可以成功地混淆以太坊交易,但政府很容易禁止它们的使用。 任何与混合器合约有互动的人,无论是发送者还是接收者,都可以被标记。 然而,这个 EIP 试图通过要求零智能合约交互才能发送资金来最小化发送者的隐私泄漏,因此我们只使用普通的 EOA 到 EOA 的转移。 为了拥有一个“传送”机制,我们将所有 Secp256k1 点 $E(K)$ 的集合划分为两个子集/地址空间:
-
可花费地址空间:$\{p \in \{0,1\}^{160} \exists s : keccak(s \times G)=p \lor \exists c : keccak(c_{params})=p \}$ -
不可花费地址空间:$\{p \in \{0,1\}^{160} \nexists s : keccak(s \times G)=p \land \nexists c : keccak(c_{params})=p \}$
可花费/不可花费的地址是无法区分的,因此我们可以利用这个事实,并为发送到无法使用常规椭圆曲线签名花费的地址的资金定义一个可花费性规则。
存款是通过将以太币发送到地址 sha256(MAGIC_ADDRESS + secret)[12:] 来进行的,在这里你知道 secret。 这确保了除非你透露,否则没有人知道存款:链上的存款与向朋友发送以太币是无法区分的。 取款使用零知识证明来证明先前存款的 secret 的知识。
存款可以来自两个地方:通过发送以太币进行的标准存款,或零钱存款。 当从另一个存款(标准或零钱)中提取资金时,会自动生成零钱存款。 不需要提取整个存款金额; 例如,如果存入了 20 个以太币并且只提取了 12 个,系统将创建一个新的 8 个以太币的零钱存款。 此零钱存款的值被哈希,因此查看链的人无法计算 零钱存款值 + 提款值 = 旧存款值 并使用该值来暴露原始存款。
此 EIP 要求 EIP-7708 以及一个将收据更改为 SSZ 格式的 EIP。 这简化了证明,因为它只需要验证一个验证日志的常规 SHA256 Merkle 分支,不需要在证明中执行 verkle 或 RLP 或其他复杂的逻辑。 该日志可以有两种类型:EIP-7708 ETH传输日志,或此 EIP 通过零钱机制本身生成的日志。
该 EIP 还包括一个隐私池1机制,你必须提供一个隐私池根,该根对应于存款列表,因此你证明你的存款是该列表中的存款之一。 由于这种第二种 Merkle 分支机制已集成到证明中,因此用户使用它的边际成本为零,从而确保了最大程度的采用并最大限度地提高了负责任地使用隐私的机会。 隐私池论文更详细地解释了这个隐私池机制的价值,以及几乎所有合法用户都使用它的生态系统的好处。
内置的 PoW 和 32 ETH 限制可以防止哈希冲突攻击:如果攻击者可以找到冲突 (x1, x2) 使得 create2_address(..., x1) == sha256(MAGIC_ADDRESS + x2),攻击者可以提取两次他们的以太币(但只能提取两次)。 这种攻击需要大约 2**80 个哈希。 添加 24 位的 PoW 将难度增加到 2**92。 可以计算出这么多哈希,但这非常困难; 目前,挖掘一个比特币区块需要大约 2**79 个哈希,因此那些有能力攻击此机制的人在比特币挖掘中有一个更有利可图的机会。
可扩展性含义
如果电路能够同时在单个证明中重新铸造多个销毁的总和,那么商家和 CEX 将能够接受他们的销毁地址付款,并通过在区块链上存储单个证明(和一堆 nullifier)将他们的资金累积到一个地址,这将大大减少区块链上的交易数量。 将使用此 EIP 作为可扩展性解决方案的人员也将增加协议的隐私保证。
向后兼容性
使用 mint 函数生成的以太币与原始以太币不应有任何差异。 人们应该能够使用那些铸造的以太币来支付 gas 费。
参考实现
待确定 (TBD)。
ZK-SNARK 实现
同样 TBD,但该实现必须满足以下条件:
- 使用透明设置; 理想情况下,重用 EIP-4844 设置。 不要使用 Groth16 或其他特定于应用程序的设置。
- 该电路应该可以直接验证,而无需盲目信任像 Circom 这样的编译器。 SHA256 并不太复杂,可以使用 PLOOKUP 和一个 8 位查找表来实现算术运算,这应该足够了。 没有必要过度关注证明者的效率。
- 仍然必须使用椭圆曲线; Stark 太大了。
安全注意事项
如果此 EIP 的实现出现错误,人们可能会铸造无限量的 ETH,从而导致以太坊价格崩溃。
版权
在 CC0 下放弃版权和相关权利。
-
{ "type": "article", "id": 1, "author": [ { "family": "Buterin", "given": "Vitalik" }, { "family": "Illum", "given": "Jacob" }, { "family": "Nadler", "given": "Matthias" }, { "family": "Schär", "given": "Fabian" }, { "family": "Soleimani", "given": "Ameen" } ], "DOI": "10.2139/ssrn.4563364", "title": "Blockchain Privacy and Regulatory Compliance: Towards a Practical Equilibrium", "original-date": { "date-parts": [ [2023, 9, 6] ] }, "URL": "https://ssrn.com/abstract=4563364", "custom": { "additional-urls": [ "http://dx.doi.org/10.2139/ssrn.4563364" ] } }
Citation
Please cite this document as:
Keyvan Kambakhsh (@keyvank), Hamid Bateni (@irnb), Amir Kahoori