作者:Shinobi
来源:https://bitcoinmagazine.com/technical/bitcoin-silent-payments-secret-keys
比特币是整个数字时代在价值转移上出现的最大突破之一。它不需要中介,其安全性由去中心化的矿工多数来保证,而且每一笔交易都会得到网络的每一个参与者的验证。这个系统的架构的设计目标是让你无论身在何方,都能收到另一个人的支付(同样无论 TA 身在何处)。众筹、慈善、为任何你想要的东西注资,都可以马上实现,不必得到任何人的许可,也不必跟任何看门人打交道,也没有繁琐的流程。在理论上,这是个天才的想法,但在实际上,它有一个很大的缺点:隐私性。
作为一个基于 “push(推送)” 的支付系统(没人能从你手上 “pull(拉取)” 支付,你必须自己明确授权一笔支付然后 “推送” 给其他人),比特币要求发送者获得一些必要的信息来定义金钱流动的目的地。这就需要接收方用某种办法告知发送方自己的比特币地址。 在想要从公众手中筹集资金时,这会给接收方的隐私性造成很大的影响,或者,需要持续在线,跟发送方保持互动。的确,任何人都可以在互联网的任何地方公开一个地址,而任何希望给这个人发送资金的人都可以把资金打到这个地址,但这样做是毫无隐私性可言的。只要在区块链上搜索一下这个地址,你将不仅能看到这个人筹了多少钱,还可以看到每个给这个地址打了钱的账号在区块链上留下的足迹。筹钱的和打钱的都没有隐私可言,所有东西都会完全公开并关联起来,被全世界看到。
若是又想公开静态地址,又想避免地址重用,那就需要运行一直在线的服务器,使得每次有人想捐钱时都给 TA 一个尚未用过的地址。虽说现在是数字时代,一直在线的服务器看起来不是什么问题,但它毕竟还是有成本的,而且增加了复杂性,尤其是有人想在家里或者自己的硬件上运行的时候。要是这人只有手机怎么办?在现在的手机操作系统上,想要最优化电池的利用来保证程序可以在后台运行一整天,几乎是不可能的;就算能做到,也会把电池用光。
BIP47
Justus Ranvier 提出了 BIP47 来解决这个问题。这个提案的目的是产生一种方法,让接收支付的一方能向自己选择的人公开足够多的信息来接收支付,但这些公开信息又不至于多到可以(1)跟踪这个公开信息的人收到了多少钱;以及(2)曝光给这个人打钱的人的任何信息。核心想法是,将接收方公开发布的信息(或者叫 “支付码”)与发送者自己的支付码结合起来、产生一组新地址,并且接收方能够为这些地址构造私钥。这些新地址是专属于这个发送者和这个接收者的,每次一个新的发送者利用这套协议来给某个接收者发送资金,协议都会生成一组新的、专属于他们俩的地址。
概要来说,这个流程一般如下:接收方先从自己的层级式确定性钱包(HD wallet)用新的衍生路径产生一个新的扩展公钥(extended public key),并公开这个公钥。这个公钥就是接收方的 “支付码”。支付方拿到这个支付码之后,就已经拥有了生成一个新地址并向该地址支付的所有必要信息了。问题在于,发送者需要告知接收者自己的支付码信息,不然他们就没法生成实际能够花费这些资金的私钥。这需要一种特殊的 “通知交易”。
假设 Alice 想用支付码跟 Bob 转账。Alice 选出一个 UTXO,发送到 Bob 的 “通知地址”;这时候,Alice 需要拿这个 UTXO 的私钥和 Bob 的通知地址的公钥相乘,产生一个隐藏秘密的私钥(secret blinding key);Alice 拿这个隐藏秘密的私钥来加密自己的支付码,并将密文放到一个 OP_RETURN 输出中。这意味着,拥有自己的通知地址的私钥、知晓 Alice 所花费的 UTXO 的公钥的 Bob,是唯一一个能解密并阅读其中的信息的人。这是因为 Alice 的私钥与 Bob 的公钥相乘,与 Bob 的私钥与 Alice 的公钥相乘的结果相同。
Alice 和 Bob 现在可以推导出一组只有他们俩知道的地址,以后,Alice 可以每次使用一个新地址给 Bob 支付任意次,外人都不能知道他们之间的关联。
这套协议的一个变种是:Alice 不是把输出发到 Bob 的通知地址上,而是创建一个找零输出,这是一个 1-of-2 的多签名输出,一个密钥是自己的找零地址,另一个是 Bob 的支付码标识符。
第三个变种使用一个 1-of-3 多签名输出来代替 OP_RETURN 编码必要的信息。除此之外,别的都差不多。
BIP47 的一个缺点是,它需要利用区块空间来发送一笔特殊的交易,在实际的支付发生之前,告知接收方他们会收到钱。所以,要是某人只准备发送一笔支付,这样做就太浪费了。而且,如果这笔用于通知的 UTXO 会跟支付给某人的 BIP47 地址的 UTXO 有关联,也会有损害隐私的风险。人们必须小心保证这两样东西相互隔离,不要在链上留下可以被跟踪的痕迹、不要让人把不同支付的 UTXO 所有权联系起来。
静默支付
“静默支付(Silent payments)” 是 Ruben Somsen 最新的想法。它本质上跟 BIP47 解决了同样的问题,而且不需要发送通知交易,但需要扫描更多的交易来侦测发给给接收者的款项。整个思路跟 BIP47 几乎完全一样:你(作为接收者)公开一部分信息,然后,发送者可借此构造一个新的、只有你能重构出来的地址。区别在实现的细节中。
接收者先在发送者知晓的地方公布一个 “静默” 公钥,然后发送者使用自己想要花费的输入的私钥来调整这个公钥。做法就是把发送者的私钥与接收者的静默公钥相乘,再加一次静默公钥。这就可以产生一个新地址,而接收者可以通过拿自己的私钥与发送者输入的公钥相乘、再加上静默公钥,就可以构造出来了。就这么简单。
但它有一个很大的缺点,是很难为此实现轻客户端支持,因为接收者必须扫描每个区块中的每一笔交易,并计算每一个输入与自己的密钥的组合,来检查它是否与交易的某个输出相匹配。对一个全节点来说,增加的验证成本是可以承受的,但对一个没有全节点的轻节点开说,就太昂贵了。
可以通过只扫描 UTXO 集来进一步优化。来自 Blockstram 的 Jonas Nick 使用英特尔 i7 CPU 运行了一项基准测试,他发现需要三个半小时才能扫完整个 UTXO 集并运行相应的地址检查计算。这还不包括查找创建这些 UTXO 的交易以获得它们的公钥的时间(这些公钥是运行地址检查计算的必要条件)。这方面还没有基准测试,所以其开销和运行时间依然是一个未解决的问题。
还有一种优化是使用发送交易的每一个输入的公钥,来做调整项。这样就不需要单个单个地扫描一笔交易的每一个输入来检查自己是否收到了钱,从而能降低扫描成本。但是,这会增加 CoinJoin 交易的复杂性,因为它将需要每个参与者都主动参与密钥调整的过程。在较为粗糙的实现中,这样也会向其它的 CoinJoin 参与者暴露你的支付目标。不过,这样做可以防止接收者知道哪个输入是用来支付给自己的;而且,只要在密码学上盲化跟其他 CoinJoin 参与者共享的信息,就可以防止他们知道哪个输出是静默支付,从而减轻所有的隐私隐患。
此外,还可以在密钥衍生流程中加入一把扫描密钥和花费密钥,使得接收方只需要让探测入账支付的密钥在线,而让可以花费资金的密钥保持离线并放在冷存储中。这需要改变密钥衍生的流程,将发送者的输入私钥与扫描密钥相乘,然后加上花费密钥。这样接收的一方可以获得更好的安全性,即使接收者的设备被攻破也只会让隐私置于风险之中,不会导致资金丢失。
还需考虑的最后一个主要问题是发送者这边的地址重用。在基本的实现中,如果发送者的同一个公钥有多个 UTXO,并且多次使用这些 UTXO 对同一个人发起静默支付,那么它会生成相同的静默地址,然后构成地址重用。在方案中加入 TxID 和输入的索引号来防止,这两个数据都可以在发送给轻客户端之前计算出来,不会增加后者的计算负担。
总体来说,静默支付在所有方面都比 BIP47 有了长足的进步,除了接收方为了扫描自己收到的支付而需付出更高的验证成本。它也保持了 BIP47 的确定性恢复的特性,在发送给同一个接收者的支付中实现了不可关联性,并消除了在实际支付之前需要先发送通知交易的需要。Somsen 又一次为一套可以提升比特币有用性的协议提出了一个非常可靠的思路。
(完)
Shinobi2022-05-02
https://www.btcstudy.org/2022/05/02/bitcoin-silent-payments-secret-keys/