本文介绍了比特币静默支付的工作原理,它通过一种新的地址格式,允许用户重复使用静态地址,同时保护隐私。文章详细解释了椭圆曲线Diffie-Hellman密钥交换(ECDH)在创建共享秘密值中的作用,以及如何利用这一技术锁定资金,确保只有接收者才能花费。
作者:benma & Sebastian
来源: https://blog.bitbox.swiss/en/understanding-silent-payments-part-one/
我们骄傲地宣布, BitBox02 是第一款能够安全地执行比特币 “静默支付” 的硬件签名器。在这个系列博客中,我们希望展示静默支付的工作原理,以及在表象之下,BitBox02 做了什么,来让这些支付能够安全地执行。
发送到静默支付地址的支持将在未来的更新中提供给所有 BitBox02 用户。
静默支付 是一个提议新的比特币地址格式的 BIP(比特币优化提议)。它的主要好处是能产生一种静态的地址。它消除了管理多个地址的需要,同时还能保护隐私性。如果你想了解更多基础概念,可以看看 我们上一篇关于可复用支付码的文章。
当前,为了保护隐私,链上支付的收款方必须在每一次收取支付时,给支付方提供一个新的地址。者导致了糟糕的用户体验,以及使用困难。
举个例子:
许多比特币服务端和用户都不想处理这样的复杂性,所以他们直接重复使用同一个地址,这就牺牲了他们的隐私性,因为他们所有的交易都会关联到同一个地址,很容易被识别和分析。
静默支付的目标是解决上述挑战。其地址的设计目标是可以复用(静态),但发送到静默支付地址的交易将只有发送者和接收者(地址的主人)才能识别出来。所以慈善机构可以在网页中公开一个稳定不变的捐款地址,而不让任何人知道谁捐了钱、捐了多少。交易所也可以为你所有的取款登记一个静默支付地址。
比特币的交易由输入和输出组成。
输出会产生新的 UTXO(未花费的交易输出),而输入则是花费现有的 UTXO 。如果关于 “UTXO” 你想了解更多,可以看看我们 关于这个主题的文章。
最常见的情况是,一个 UTXO 是用一个 公钥 来锁定的,而要花费它的时候,需要提供一个由该公钥背后的 私钥 所生成的签名。当你为比特币交易填入一个地址的时候,通常,这个地址就编码了一个公钥(或者一个公钥的哈希值)。
静默支付使用了一些有趣的密码学技术,为发送给一个静默支付地址的每一笔交易都派生一个新的、没有用过的公钥。现在,让我们来了解一下这背后的知识和直觉。下文是对静默支付工作原理的简化的、概要的描述。想要了解所有的细节,还是得看 它的 BIP 本身。
私钥就是一个很大的数字,长到无法猜测出来,比如说:
9219380591327707100805598430331919161419734194966460287451189885463363544321492193805913277071008055984303319191614197341949664602874511898854633635443214
每一个私钥都能生成一个对应的公钥。公钥不会暴露其背后的私钥的任何信息。
从技术上来说,公钥属于一个数学上特殊的 “群”,它有自己专门的假发。如果你想了解更多,《 椭圆曲线密码学:通俗介绍 》这个系列博客是非常好的材料。
从私钥生成公钥的公式是:
公钥=私钥×G公钥=私钥×G
这里的 GG 是预定义的值,称为 “生成元”。生成元是一个特殊的公钥。所有其它的公钥,都是通过生成元和加法,生成出来的。
比如说,如果你的私钥是 22 ,那么,你的公钥就是 2×G=G+G2×G=G+G 。如果你的私钥是 33 ,那你的公钥就是 3×G=G+G+G3×G=G+G+G ,依此类推。 当然,在实际用途中,你的私钥会是一个非常大的、怎么猜也猜不到的数字,就像上面那个例子。
除了不能反向计算,大部分常用的数学,在公钥所在的群里也是一样的。比如大家熟悉的 “结合律”,依然成立:
(a+b)×G=a×G+b×G(a+b)×G=a×G+b×G
在本文剩余的章节中,我们用小写字母来表示私钥,用大写字母来表示公钥。例如: a×G=Aa×G=A 。
“椭圆曲线上的 Diffie-Hellman 密钥交换( ECDH)” 是一套简单的协议,为两个参与者 Alice 和 Bob 创建一个他们共享的秘密值。
我们假设 Alice 要给 Bob 发送一笔交易。
只需要把自己的公钥交给对方,他们就可以创建一个只有他们俩知道的秘密值。
你可以看出,两个数值是一样的:
S=a×B=a×(b×G)=a×b×G=b×a×G=b×(a×G)=b×AS=a×B=a×(b×G)=a×b×G=b×a×G=b×(a×G)=b×A
技术上来说,SS 是私钥 a×ba×b 所对应的公钥,可以用在交易的输出中,作为锁定输出的公钥,但是,需要让 Alice 和 Bob 的私钥结合起来,才能花费它。
为了避免分享私钥,我们可以通过哈希算法,将它转化为一个新的私钥:s=hash(S)s=hash(S) 。从而,我们可以用这样的公钥来锁定输出:
P=hash(S)×G=hash(a×B)×G=hash(b×A)×GP=hash(S)×G=hash(a×B)×G=hash(b×A)×G
被这个公钥锁定的资金,可以被发送者和接收者任何一方(使用自己的私钥)花费。那怎么做到让它只能被 Bob 花费呢?接着往下看。
静默支付地址的前缀为 sp1
(而不是我们熟悉的 bc1
),而且,它看起来是这样的:
sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwvsp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv
非常长对吧?因为它编码了接收者的 两个 公钥:一个用于扫描的公钥 BscanBscan,一个用于花费的公钥 BspendBspend 。额外的公钥,是为了让只有 Bob 才能花费。
在发送交易的时候,Alice 为 Bob 安排的输出所用的公钥不是 P=hash(S)×GP=hash(S)×G,而是 加入 了 Bob 的第二个公钥的调整版本:P=Bspend+hash(S)×GP=Bspend+hash(S)×G 。至于 SS ,Alice 用 Bob 的扫描公钥和自己的私钥计算出来:S=a×BscanS=a×Bscan 。
Alice 能计算出公钥 PP,是因为她从 Bob 的静默支付地址中知道了 BscanBscan 和 BspendBspend ,而且她知道自己的私钥 aa 。
最终的公钥 PP 会用来锁定添加到交易的一个 Taproot 输出。
只有 Bob ,才能够用自己的两个私钥 bspendbspend 和 bscanbscan 来花费这个输出中的资金,因为只有他才知道 PP 背后的私钥:
P=Bspend+hash(S)×G=bspend×G+hash(bscan×A)×G=(bspend+hash(bscan×A))×GP=Bspend+hash(S)×G=bspend×G+hash(bscan×A)×G=(bspend+hash(bscan×A))×G
所以,Bob 最后用来花费这个输出的私钥就是:p=bspend+hash(bscan×A)p=bspend+hash(bscan×A) 。
Bob 使用自己的扫描私钥计算出共享秘密值的哈希值 hash(S)=hash(bscan×A)hash(S)=hash(bscan×A) ,再加入自己的花费私钥,就可以计算出最终的私钥。
Alice 想给 Bob 支付,她知道 Bob 的公钥,因为 Bob 所公开的静默支付地址里面有。
那么 Alice 的密钥呢?Bob 怎么知道 Alice 所用的公钥?不知道 Alice 的公钥,不就没办法重新创建出共享秘密值、也无法探测出发给他的静默支付交易、更没法花费资金了嘛?要是接收者还需要跟每个支付者沟通、获得对方的公钥,那就很不方便。
解决方案非常优雅:所有附加到交易本身的有关密钥!
Alice 花费自己的 UTXO、制作发送给 Bob 的交易;这些 UTXO 本身分别被一个 公私钥对 锁定。她需要自己的私钥来花费自己的资金,那何不直接使用这些私钥来创建共享秘密值呢?
所以,Alice 拿自己用作交易输入的所有 UTXO 的私钥,来创建用在静默支付中的共享秘密值。
Bob 可以通过所有交易输入的公钥,计算出共享秘密值。
一笔交易可能有多个输入,所以 Alice 要使用一个结合私钥,就是所有输入的私钥之和:
a=a1+a2+…a=a1+a2+…
Bob 也要使用交易的结合公钥,也就是所有交易输入的公钥之和:
A=A1+A2+…A=A1+A2+…
根据 结合律,这些私钥的和正是公钥的和所对应的私钥:
A=(a1+a2+…)×G=a1×G+a2×G+…=A1+A2+…A=(a1+a2+…)×G=a1×G+a2×G+…=A1+A2+…
为了实现上述效果,每一个输入都必须有且仅有一个相关的公钥。因此,静默支付仅允许使用 下列脚本类型 的输入:P2TR(且要求是密钥花费)、P2WPKH、P2WPKH-P2SH、P2PKH;换句话说,之允许使用单签名的脚本类型,这也是当前最常见的类型。不幸的是,如果 Alice 用的是一个原生的多签名脚本(而不是 MuSig2 或其它能够聚合成单个公钥的聚合签名方案),或者是其它高级脚本,就无法发起静默支付。我认为这是静默支付的一个很大的缺点,也是阻止其得到采用的巨大障碍。
静默支付的主要目标是避免 地址/公钥 复用。根据上述方案,接收者的公钥是从 Alice 在交易输入中的公钥和 Bob 的静默支付地址中动态创建出来的。然而,如果 Alice 一直使用同一个地址,那么她发送给 Bob 的静默支付地址的多笔交易,会因为使用相同的输入公钥而产生相同的输出公钥。
为了解决这个问题,静默支付提议规定,输出公钥并不像上文那样计算(P=Bspend+hash(S)×GP=Bspend+hash(S)×G);而要这么计算:
P=Bspend+hash(inputhash×S)×GP=Bspend+hash(inputhash×S)×G
这里的 inputhash=hash(outpoint||A)inputhash=hash(outpoint||A) ,也就是交易的其中一个输入的输出点与公钥的字节拼接的哈希值。输入的 “输出点” 是一个交易 ID 和一个输出索引号,是 UTXO 的唯一标识符。因为 UTXO 在比特币区块链上无法重复花费,那么 UTXO 的唯一标识符的哈希值就保证了独一无二的输出公钥,因为没有两个 UTXO 会有同样的标识符。出于 BIP 所说的一个 技术理由,AA 也放在哈希函数中。
在提议的最初草案中,input_hash 是根据所有交易输入(在运行字典排序后)计算出来的。在我的评审意见中,我 指出 这对硬件签名器是个挑战(点击页面中的 “Show resolved” 来展开帖子:
这对硬件签名器是个挑战。硬件签名器的内存有限,也许无法加载所有的输出点并加以排序。因此,将不得不限制交易为在签名时刻已经有排好序的输入,这是不理想的,而且会产生兼容性问题。
与其使用排好序的输出点的哈希值,也许我们可以使用 ……
在许多人参与讨论之后,能够满足要求、也适合硬件签名器的解决方案出现了:仅哈希一个输入,在字典排序中最小的哪个输入。这产生了该 BIP 的最终版本,并让 BitBox02 可以无摩擦地实现对静默支付的支持。
我们介绍了静默支付的概念;作为一个比特币优化提议,它提出了一种新的地址格式,让用户可以重复使用一个静态地址,同时保护隐私性。我们介绍了私钥和公钥的基本概念,讨论了使用 ECDH 来创建共享秘密值的过,以及这种技术如何用来锁定资金、保证只有接收者才能花费它们。
在下一篇文章中,我们将了解静默支付如何改变了硬件签名器的角色,以及为了安全地支持静默支付 ,硬件签名器要做哪些事情。
- 本文转载自: btcstudy.org/2025/08/06/... , 如有侵权请联系管理员删除。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!