函数隐私:从零币到可委托 DPC
深度解析 ZEXE 协议的设计理念和技术实现,理解可编程隐私的实现机制。
目录
介绍
> ZEXE 是一个基于账本的系统,它允许在同一个账本上运行多个应用程序,且交易不会泄露所执行特定计算的信息。
引言
ZEXE 是一个基于账本的系统,它允许用户离线执行任意计算,并通过使用零知识证明来生成证明这些计算正确性的交易。ZEXE 的方法旨在实现不仅是数据隐私(隐藏所有用户数据),还包括功能隐私,即观察者无法区分离线执行的函数。离线隐私计算可能代表各种应用程序的状态转换(例如代币、市场、选举等),所有这些应用程序都运行在同一个账本上。
从零币到零现金及更远
零硬币
提高加密货币匿名性的方法之一是使用混淆用户交易历史的混合器。为了混合,用户将他们的硬币发送给中介,并取回其他用户存入的不同硬币。Zerocoin 被设计为比特币的去中心化混合器。通过一系列交换,如比特币→Zerocoin→比特币,用户用相同数量的新硬币替换旧硬币,而这些新硬币无法与发送到混合器的硬币关联。这种不可关联性是通过使用零知识证明和承诺方案实现的。
比特币 → 零硬币
要从比特币铸造一个 Zerocoin,你需要生成一个序列号 $sn$ (该序列号唯一标识硬币),使用一些随机性 $r$ 创建一个承诺 $COMM_r(sn)$ ,并向账本发送一个承诺交易。
零硬币 → 比特币
要赎回一枚硬币,你必须在零知识证明中证明你知道一个值 $r$ ,使得 $COMM_r(sn)$ 存在于账本上。你将证明放入交易中,并附带序列号 $sn$ ,然后将其发送到账本。由于 $r$ 在零知识证明下是隐藏的,因此不可能知道已赎回了哪枚已提交的硬币,结果也不可能找到与初始比特币的关联。发布 $sn$ 确保硬币不会被重复花费。
Zerocash
Zerocash 是一种数字货币方案,允许直接支付任意金额,并且基于 Zerocoin。为了基于 Zerocoin 协议创建一个完整的支付系统,Zerocash 引入了一些功能,例如从一名用户向另一名用户发送硬币的能力,或者只需一次交易即可发送任意数量的某种特定硬币。Zerocash 通过隐藏硬币值和用户身份,提供了强大的数据隐私保证。ZEXE 可以被视为 Zerocash 的一种泛化,它实现了数据和功能隐私。
函数隐私到底是什么?
在 Zerocash 中,每笔交易都包含一个零知识证明,用以证明该交易中的价值守恒,即所有花费的硬币价值之和等于所有创建的硬币价值之和: $sum_{i} v_i^{old} = sum_{j} v_j^{new}$ 。如果 Alice 想给 Bob 和 Charlie 分别送 $x$ 枚硬币,Bob 从 Alice 那里收到 $y$ 枚硬币,Charlie 从 Alice 那里收到 $z$ 枚硬币,那么必须满足 $x = y + z$ 。价值守恒谓词代表了一种对硬币价值进行的计算。现在假设我们有两个用户自定义的资产, $A_1$ 和 $A_2$ ,它们使用同一个账本来存储交易(例如支持许多合约的以太坊平台,每个合约代表一种不同的货币)。即使它们都采用了类似 Zerocash 的协议来实现数据隐私,交易仍然会揭示具体交换了哪种代币, $A_1$ 还是 $A_2$ ,换句话说,执行了什么计算。函数隐私指的是无法区分一种计算与另一种计算。在这种情况下,函数隐私将不成立。 在 Zerocash 中,函数隐私是通过简单的原因实现的:每个交易中只执行一种类型的计算,这意味着通过查看交易中执行的计算无法区分交易。
实现函数隐私
我们希望能在同一账本上进行多种类型的计算。但如果我们只是开始执行各种计算而不做额外修改,函数隐私将无法保持。为了实现具有多种交易函数的账本的函数隐私,引入了一种名为去中心化隐私计算(DPC)的新密码学原语。DPC 可以看作是 Zerocash 方法的推广,它使得在任意数据单元上执行任意计算的匿名化成为可能。
从硬币到记录
一个记录是具有任意有效负载的数据单元。记录是硬币的泛化概念,但记录持有的不是整数硬币值 $v$ ,而是任意有效负载 $p$ 。像硬币一样,记录有所有者,可以被创建和消耗,并且每笔交易都有记录作为输入和输出值(如在 UTXO 模型中)。如同 Zerocash 一样,要创建一个记录,你必须向账本发送一个承诺,当记录被消耗时,记录的序列号被揭示,而相应的随机性则通过零知识证明隐藏。记录可用于各种类型的计算。在存在多个函数的情况下,为了实现函数隐私,我们需要一种方法使这些计算彼此不可区分。这里的技巧是使用一个通用函数,并将“真实”函数作为记录的有效负载存储。那么系统中的所有交易都只实现一个函数,即通用函数,而所有与真实函数相关的细节都隐藏在有效负载中。记录纳米内核(RNK)定义了一组在记录上进行计算的规则。
记录纳米内核(RNK)
每条记录都有一个有效载荷、一个出生谓词 $Phi_b$ 和一个死亡谓词 $Phi_d$ 。当记录被创建时,必须满足出生谓词,当记录被消费时,必须满足死亡谓词。RNK 确保这两个条件都得到满足,以便在整个记录的生命周期中,强制执行特定的约束。谓词可以看到整个交易的全部内容,其中包括:
* 每条记录的内容(其有效载荷、出生和死亡谓词等)
* 交易备忘录:在账本上发布的共享内存(例如承诺、已消费记录的序列号)
* 辅助输入:未在账本上发布的共享内存
根据输入数据,谓词可以决定哪些类型的记录交互是被允许的,哪些是被禁止的,这使得记录能够以安全的方式相互通信。例如,一个记录可以选择不与那些不满足特定属性的负载的记录交互,或者只允许与具有特定类型的出生或死亡谓词的记录交互。只有当所有被消耗记录的死亡谓词和所有被创建记录的出生谓词同时得到满足时,一个交易才被认为是有效的。为了证明其有效性,每个交易包含一个零知识证明,用以证明这些谓词的满足情况。为了实现效率,使用了一层递归证明组合。这意味着实际上存在两个证明:内部的证明证明谓词按要求得到满足,外部的证明证明内部证明是有效的。内部证明可以在离线状态下计算,而外部证明确保其计算正确。内部和外部证明都是简洁的,这意味着验证它们是低成本的。 外层证明必须是零知识证明,而内层证明则不必。
详细记录
每条记录都有其自己的地址公钥 $apk$ ,用于指定记录的所有者。与 Zerocash 类似,要创建一条记录,必须创建一个包含记录数据的承诺 $cm$ ,而要消费记录,必须揭示其序列号 $sn$ ,这个序列号无法在未获得对应地址秘密密钥 $ask$ 的情况下计算出来。这意味着只有持有 $ask$ 的人才能消费该记录。地址秘密密钥 $ask=(sk_{PRF}, r_{pk})$ 由一个秘密伪随机函数密钥 $sk_{PRF}$ 和承诺随机数 $r_{pk}$ 组成。地址公钥 $apk=COMM_{r_{pk}}(sk_{PRF})$ 是一个完美隐藏的承诺,用于对 $sk_{PRF}$ 进行加密,并使用随机数 $r_{pk}$ 。密钥 $sk_{PRF}$ 用于计算记录的序列号: $sn=PRF_{sk_{PRF}}( ho)$ ,其中 $ ho$ 是一个序列号随机数,也存储在记录中。总而言之,一条记录包含以下内容:
* 地址公钥 $apk = COMM_{r_{pk}}(sk_{PRF})$
* 出生谓词 $Phi_b$
* 死亡谓词 $Phi_d$
* 数据有效载荷
* 序列号 nonce $ ho$
* 对以上所有内容进行承诺 $cm$
虚拟记录
虚拟记录是一个源自旧协议的功能,在这些协议中,每笔交易的输入和输出数量是固定的。在 ZEXE 中,可以自由创建虚拟记录,但消耗它们需要满足其死亡谓词。虚拟记录中存储的数据不会被检查,也不会在任何地方使用。虚拟记录的一个优点是它们隐藏了交易的真正输入和输出数量,因为没有机制可以区分虚拟记录和非虚拟记录。用户只能知道交易中创建/消耗的非虚拟记录数量的上限。虚拟记录也可能对谓词中实现某些逻辑很有用。
可委托的 DPC
为确保出生和死亡谓词得到满足,用户必须提供相应的零知识证明,这对弱设备用户(例如手机用户)来说是个问题。可委托的 DPC 方案旨在解决这个问题。可委托的 DPC 方案允许用户将证明的创建委托给工作者,然后将证明包含在交易中。其核心思想是将密钥 $ask$ 拆分,使得用于生成加密证明的部分与用于授权交易的部分分离,然后将第一部分交给工作者,将第二部分隐藏起来。这样,工作者只能生成证明,但由于工作者无法访问密钥的剩余部分,因此无法使用该证明来创建交易。为实现这一目标,使用了可随机化签名。可随机化签名允许公钥和签名的随机化,以防止跨多个签名进行关联。每个用户被分配一个签名密钥对 $pk_{SIG}, sk_{SIG}$ ,其中 $pk_{SIG}$ 嵌入到 $apk$ 中,而 $sk_{SIG}$ 仅用于交易授权。 现在用户将生成证明所需的数据(包括密钥 $sk_{PRF}$ )发送给工作者,使用密钥 $sk_{SIG}$ 对生成的证明进行签名,对结果签名进行随机化,并将其包含在最终交易中。由于工作者不知道签名密钥 $sk_{SIG}$ ,因此无法生成有效的交易。
可随机化签名
随机化签名方案背后的思想是,每当我们计算一条消息的签名时,我们都会同时更新公钥和签名,以便随机化的公钥可以用来验证随机化的签名,但它们的值都与之前的不同。例如,考虑一个 Schnorr 签名方案。假设我们有一个签名密钥对 $(pk=g^x, sk=x)$ ,签名计算为 $sigma = (s = k - xe, e = H(g^k || m))$ ,其中 $k$ 是一个随机标量。我们可以使用随机值 $r_{SIG}$ (在 ZEXE 的情况下为 $r_{SIG} = PRF_{sk_{PRF}}( ho)$ )来随机化公钥和签名,如下所示: $hat{pk} = pk * g^{r_{SIG}}$ $hat{sigma} = (s - e*r_{SIG}, e)$ 。
不正式地说,对于一个签名 $(s, e)$ 和公钥 $pk_{SIG}$ ,签名验证算法看起来像:
1. 计算 $r_v = g^s {pk_{SIG}}^e$
2. 计算 $e_v = H(r_v || m)$
3. 检查 $e == e_v$
如果我们有一个随机化的签名 $hat{sigma} = (hat{s}, e)$ 和相应的随机化公钥 $hat{pk}_{SIG}$ ,公钥的随机性会抵消签名的随机性: $hat{r}_v = g^{hat{s}} * {hat{pk}_{SIG}}^e = g^{s-e*r_{SIG}} * g^{xe+e*r_{SIG}} = g^{s+xe} = g^s * {pk_{SIG}}^e = r_v$ 。注意签名和公钥必须使用相同的值 $r_{SIG}$ 进行随机化以匹配。
#### 可委托 DPC 的阈值和盲交易
可委托 DPC 方案支持创建阈值和盲交易。若存在 $n > 1$ 个持有授权密钥 $sk_{SIG}$ 份额的参与方,且其中任意 $t$ 个参与方足以对交易进行签名,则该交易称为阈值交易。由于随机化发生在密钥和签名创建之后,因此可以直接使用现有的阈值或盲签名协议,然后进行随机化处理。要使用可委托 DPC 方案生成阈值交易,必须采用支持阈值密钥生成和阈值签名算法的签名方案。要创建盲交易,则必须使用具有盲签名算法的签名方案。
用例1:用户自定义资产
总而言之,让我们考虑一些 ZEXE 的应用场景。要在 DPC 之上创建一个自定义系统,我们必须为该系统定义规则。为此,我们定义出生和死亡谓词,并描述记录负载的预期内容。因此,系统是由记录的内容定义的。首先,我们考虑一种交易格式,其中恰好有两个输入记录和两个输出记录。对于一个代表用户定义资产硬币的记录,我们创建一个名为 Mint-or-Conserve 的出生谓词。它被称为这个名字,因为它可以在两种模式下工作:铸币模式和保存模式。在铸币模式下,出生谓词创建资产初始供应量 $v$ 和唯一资产标识符 $id$ 。输出创世记录的负载是 $(id, v, v=v)$ ,其中 $v$ 是记录的值。从已消耗输入记录的序列号中推导出唯一资产标识符 $id$ : $id = COMM_r(sn_1 || sn_2)$ 。由于序列号是唯一值,绑定承诺属性保证了 $id$ 的值也是唯一的。 在保守模式下,MoC 会检查该交易中所有具有相同 MoC 且相同资产 ID 的记录,确保其值保持不变(就像在 Zerocash 中,输入记录的总值等于输出记录的总值)。因此,如果 Alice 想将一些代币发送给 Bob,Alice 会创建一个包含一个消耗输入记录、一个虚拟输入记录和两个输出记录的交易:第一个输出记录属于 Bob,第二个输出记录属于 Alice,并代表剩余的代币。
用例2:私有 DEX
现在我们能够创建用户自定义资产,那么如何交换它们呢?我们将使用去中心化交易所(DEX),这是一个基于账本的应用程序,允许交易数字资产。为了实现一个私有的 DEX,除了我们定义的 MoC 出生谓词,我们还定义了交换或取消(EoC)死亡谓词。在交换模式下,EoC 允许通过将其兑换为其他某种资产的单位来消耗记录。在取消模式下,EoC 允许通过将代币发送到指定地址(回到所有者那里)来取消交换。
基于意图的 DEX
在基于意图的 DEX 中,存在一个索引表,其中制造者发布他们的交易意图(例如,用 5 单位的资产 $A_1$ 交换 10 单位的资产 $A_2$ )。感兴趣的买家 T 可以联系制造者 M,商定条款,并生成一笔交易。
1. M 发布交易意图,用 5 单位的 $A_1$ 交换 10 单位的 $A_2$
2. T 联系 M 商定交易条款,创建一个包含 10 单位 $A_2$ 和 EoC 死亡谓词的记录,该谓词指定该记录只能在使用 5 单位的 $A_1$ 交换后才能花费,并将所有赎回记录所需的信息发送给 M
3. 现在,M 可以在交换模式下使用 EoC 谓词创建交换交易,或在取消模式下使用 EoC 谓词取消交换。
由于谓词和记录的有效载荷在零知识证明下是隐藏的,因此不会泄露任何私人信息。 请注意,M 不必立即创建交易,在实际交换发生之前,代币价格可能会发生变化(例如,当 T 创建记录时,5 单位的 $A_1$ 相当于 10 单位的 $A_2$ ,但后来 5 单位的 $A_1$ 只相当于 5 单位的 $A_2$ ),因此交易对一方来说变得更有利可图,而另一方面则变得不那么有利可图。
基于顺序的 DEX
在基于订单簿的交易所(DEX)中,做市商会在订单簿中提前发布记录以及赎回所需的信息。在公开订单簿的交易所中,交易者从订单簿中选择一个订单,而在封闭订单簿的交易所中,订单簿操作员会链下将交易者与提供者进行匹配。
公开簿记式交易所
如果 M 希望用 5 单位的 $A_1$ 交换 10 单位的 $A_2$ ,她会创建一个包含 5 单位 $A_1$ 的记录,这个记录有一个 EoC 死亡谓词,指定该记录只能在被兑换为 10 单位 $A_2$ 时才能被使用,并将该记录连同兑换所需的信息一起发布到订单簿中。
T 可以在交换模式下使用 EoC 死亡谓词创建交换交易,或在取消模式下使用 EoC 谓词取消交换。由于谓词和记录的有效载荷在零知识证明下是隐藏的,因此不会泄露任何私人信息,但由于交易会揭示记录的序列号,它可以与订单关联(序列号是知道如何兑换记录所需的数据的一部分),这会揭示正在交换的资产和数量。
封闭式交易所
如果 M 希望用 5 单位的 $A_1$ 交换 10 单位的 $A_2$ ,她会创建一个包含 5 单位 $A_1$ 的记录,这个记录有一个 EoC 死亡谓词,指定该记录只能在被兑换为 10 单位 $A_2$ 时才能被使用,并将该记录连同兑换所需的信息发送到订单簿。
现在 T 发布一个包含 10 单位 $A_2$ 的记录,这个记录有一个 EoC 死亡谓词,指定该记录只能在被兑换为 5 单位 $A_1$ 时才能被使用,并将该记录连同兑换所需的信息发送到订单簿。
订单簿操作员创建一个交易来消耗匹配的记录。由于订单不会发布,金额和资产信息只会被订单簿操作员知晓。
最终思考
在数据隐私属性中增加了函数隐私,ZEXE 允许多个应用程序以相同的方式使用同一个账本,即一个应用程序的交易与其他应用程序的交易无法区分,从而增强了双方的无匿名性保证。引入的设计使应用程序能够通过编程出生和死亡谓词中的条款和条件来自然地相互交互。使用简洁的零知识证明进行离线计算,使系统能够高效运行,而委托选项降低了系统要求,从而扩展了能够参与设备的范围。
---
由 Yulia Khalniyazova 撰写,她是 Heliax 的零知识密码学研究专家与协议开发者,Anoma 团队的成员。
如果你对零知识密码学、前沿加密协议或 Rust 中的工程职位感兴趣,可以查看 [Heliax 的开放职位](https://heliax.dev/jobs)。