技术 › 谁发送的?

Key image

输出被花费时留下的指纹——有且仅有一次

简而言之

Key image(密钥镜像)I = x·Hp(P),其中 P 是输出的一次性公钥,x 是它的私钥。对于给定的输出,key image 永远相同,但它不会透露是哪一个输出。网络保存所有 key image,并拒绝包含重复 key image 的交易。

双花问题

在比特币中,双花很容易发现:每个输入直接指向它花费的输出,节点会把该输出记为已花费(UTXO 集合)。而在门罗币中,一个输入指向 16 个输出,并且不知道哪个是真的。所以“标记已花费的输出”是不可能的。我们需要一种方法:既能标记一次花费,又不泄露花费的是什么。

解决方案

I = x · Hp(P)

为什么这样可行:

  • 确定性。对于一个输出,x 和 P 是固定的,所以 I 是唯一的。再次花费该输出就意味着公布同一个 I。
  • 不可关联。要验证 I 与 P 是否对应,需要知道 x:外人只看到两个“随机”的点 Hp(P) 和 I,无法建立联系(这是一个 Diffie–Hellman 问题)。
  • 不可伪造。可关联环签名证明了 I 是用与环中某个公钥相同的 x 计算出来的。

为什么用 Hp(P) 而不直接用 G?那样的话 I = x·G = P——key image 就等于输出的公钥,花费会立即暴露。Hp 给出一个没有人知道其对数的点——每个输出独有的“第二个基点”。

新交易key image I = 3f9a… 节点数据库 a17c… 0be2… 3f9a… ← 已存在! … ✗ 拒绝: 双花 节点不知道哪个输出被花费了——只知道这个指纹出现过

子群检查:2017 年的漏洞

Ed25519 上的点数是 G 所在子群的 8 倍。给 I 加上一个小阶点 T(满足 8·T = 0),会得到另一个点 I' = I + T,而在某些条件下环签名仍能通过验证。一个输出——最多 8 个不同的 key image——最多花费 8 次。2017 年,这个漏洞在原始 CryptoNote 代码中被发现;门罗币在被利用之前修复了它,而 Bytecoin 则遭到了利用。

修复方法:节点检查 l·I = 0,即 I 位于正确的子群中。

Key image 与只读钱包

要知道自己的输出是否已被花费,钱包需要计算它的 key image 并在区块链上查找。这需要 x = Hs(8aR ‖ i) + b——也就是需要花费私钥。因此只读钱包看不到支出,显示的余额会偏高。变通办法是从完整钱包导出 key image。Carrot 为此增加了一把专用密钥。

FCMP++ 中的 key image

在 FCMP++ 中,key image 依然存在:L = x·Hp(O)。改变的是证明它与输出之间联系的方式——不再用环,而是用一个关于所有输出构成的树的成员证明。一个重要的结果是:RingCT 交易中已经公布的所有 key image 仍然有效——切换之后,旧输出无法被第二次花费。