一次性地址
隐身地址:向一个地址付款,却不把它写到区块链上
发送方选取一个随机数 r,公布 R = r·G,并把钱发送到一次性公钥 P = Hs(8·r·A ‖ i)·G + B。接收方用 a 计算同样的值来找到它:Hs(8·a·R ‖ i)·G + B。花费时使用私钥 x = Hs(8·a·R ‖ i) + b。
任务
我们希望:
- 区块链上既不出现接收方的地址,也不出现任何在向同一个人多次付款时会重复的信息;
- 接收方无需与发送方沟通,就能自己找到属于自己的输出;
- 只有接收方能花费这个输出。
解决方案是在每笔交易中内置一次 Diffie–Hellman 密钥交换。
分步说明
1. 发送方选取随机数 r
每笔交易都使用一个新的随机数 r(交易私钥)。它的公钥版本 R = r·G(交易公钥)被写入交易的 tx_extra 字段。
2. 共享秘密
发送方计算 8·r·A(A 是地址中的查看公钥)。之后接收方计算 8·a·R。两者是同一个点,因为 a·R = a·r·G = r·a·G = r·A。在门罗币的代码中,它被称为密钥派生(key derivation)。
因子 8(余因子)把点中可能存在的小阶分量“清除”掉:即使有人提供了恶意构造的 R,结果也会落在正确的子群中。
3. 一次性公钥
这就是一次性地址(输出公钥)。它看起来像一个随机点:没有 a 或 r,就没有任何东西可以拿来和它比较。
4. 接收方扫描
对于每笔新交易中的每个输出 i,钱包计算 Hs(8·a·R ‖ i)·G + B 并与 P 比较。匹配——这个输出就是我们的。这只需要查看私钥 a 和公开的 B——因此只读钱包也能找到收款。
5. 接收方花费
一次性输出的私钥:
这需要花费私钥 b。私钥 x 用于签署花费,key image 也由它计算得出。
金额也用共享秘密加密
金额的掩码也是从同一个共享秘密派生的。输出金额(8 字节)以 amount ⊕ Keccak("amount" ‖ Hs(8rA ‖ i))[0..8] 的形式存储,承诺的盲化因子为 Hs("commitment_mask" ‖ Hs(8rA ‖ i))。接收方计算同样的值并解密出金额。详见 Pedersen 承诺。