现在的交易格式
让我们来看一个具体的比特币交易:
bitcoin-cli getrawtransaction c0a0d0f4de3be3b57f516f127a6fa5d5895f09bfacbb001853593320fb0a4164 0100000001473e2a366b081a0e6645199692ccf2f6e25e4556a18265785a88b46d50fd68c2000000006a4730440220296dd1587b8290a04a9253de431b5dbb698030527df5ca75592ab29eea12e85b0220737cafd85b79a7dea1b29c0a5753432d1dacb50f5225221974e38134617ffc85012103dd38b9b13b24498c382045afb08a4256c3c8bf86e15122df741bd2c2a0bd120fffffffff02a0d21e000000000017a914d1640264c522d9a31aaf69207e57d0a224eb7e668770110100000000001976a914dfd6b01e5ec1ecb1568da79931ddd8327dafd9b588ac00000000 tx_id = tx_hash = c0a0d0f4de3be3b57f516f127a6fa5d5895f09bfacbb001853593320fb0a4164 raw_tx = 0100000001473e2a366b081a0e6645199692ccf2f6e25e4556a18265785a88b46d50fd68c2000000006a4730440220296dd1587b8290a04a9253de431b5dbb698030527df5ca75592ab29eea12e85b0220737cafd85b79a7dea1b29c0a5753432d1dacb50f5225221974e38134617ffc85012103dd38b9b13b24498c382045afb08a4256c3c8bf86e15122df741bd2c2a0bd120fffffffff02a0d21e000000000017a914d1640264c522d9a31aaf69207e57d0a224eb7e668770110100000000001976a914dfd6b01e5ec1ecb1568da79931ddd8327dafd9b588ac00000000 tx_hash = double_sha256(raw_tx)
这其实就是原始格式(16进制)的比特币交易:rawtx。比特币网络上传播着的就是这种交易,你发送、接收的比特币交易其实也是这个样子的。
刚才那笔交易中,签名是这样的:
30440220296dd1587b8290a04a9253de431b5dbb698030527df5ca75592ab29eea12e85b0220737cafd85b79a7dea1b29c0a5753432d1dacb50f5225221974e38134617ffc8501
可以看出,现在的交易结构中,签名是在前半部分的内容里(其实就是 TxIn 的位置)
签名为什么要在这里,在这里有什么好处?
答案是:中本聪是这么设计的,所以签名在这里。好处嘛,其实也没什么好处,不止是没有好处,缺点却还不少。
1、由于这种设计,比特币的交易结构中就凭空的隐藏了不少“秘籍”,比如说:未签名交易其实就是用了另一个叫 ScriptPubKey 的内容,先占据着签名所需的位置,然后对这个未签名的交易做签名,再把签名的结果替换进去。这其实是很麻烦的一件事,既不容易说清楚,也很难用代码去实现。当一个交易中有多个输入(TxIn)的时候,又要有一套“秘籍”来依次准备未签名交易,依次把签名替换回去;而当一个交易中又涉及到多重签名的交易时,则又需要另一套约定好的规则来准备未签名交易,再进行签名后的数据替换工作;如果一个交易既涉及多个输入,又涉及多重签名呢?那就是秘籍套秘籍,反正是麻烦的要死;有兴趣且有能力的爱好者不妨去翻翻比特币协议在各种语言中的实现部分,就会发现,无论是 C++、Java、Python 等语言所编写的比特币协议库,通常都需要写非常多的、混乱不堪的代码来做上述的交易数据构造工作,很头疼,写过的都懂。
2、将签名放在交易结构中,由于签名的可塑性,使得交易也就有了可塑性,比如说因为椭圆曲线的对称性,s和s’都是能被验证通过的签名,这种情况下,任何接收到你所广播的交易的节点,都有能力用s’来替换掉s,然后重新广播,重新广播的交易其实有着相同的内容(输入 TxIn 和输出 TxOut),但却有着不同的 tx_id (tx_hash),这样就能达到混淆视听的目的,甚至影响到一些比特币企业(如交易所)的服务。交易延展性不会直接导致您丢币,但会影响到个人和企业对于比特币交易状态的判断,进而可能导致可能出现的资产管理风险。之前,bitcoin-core 在 BIP62 中进行过一些试图努力解决交易延展性问题的尝试,但从某种意义上来讲,交易延展性其实是无法彻底根治的,至少私钥拥有者可以重新生成一个签名,交易不变但签名变了,最终的 tx_id 也就变了,这种情形应用于多重签名的场合,甚至可能会影响到其它钥匙的持有人对于交易的判断,导致一些潜在的资产风险,在当前的交易结构下,我们是无法彻底杜绝这种情况的发生的;
3、当我们需要对交易进行签名时,对每个 TxIn 进行签署的 hash 都是不同的(参见上述的“秘籍”),所以对于“冷钱包”这种离线签名的实现方式其实增加了很大的难度,无论是数据结构还是代码的层面;
隔离验证的签名
从上面的分析可以看出,将签名放在交易内容中其实没有任何好处,反而增加的结构的复杂度和交易延展性的风险,这显然是得不偿失的。那么,最合理的交易结构到底应该是什么样子的呢?其实很简单:
TxIns + TxOuts + [签名]
也就是说把签名单独拿出来,放到外面就能解决上述的所有问题了。这就回到要谈论的标题 – “隔离验证”。隔离验证其实就是把原来的比特币交易中签名的部分单独拿出来放到另一个叫 witness 的结构中,来达到所说目的。看到这里,大家就明白了吧,通常一个看起来很复杂的课题“隔离验证”,其基本原理其实并不复杂,所要达到的目的其实很简单,就是要把签名拿出来,放到另一个地方,也就是所谓的“隔离”,就这么简单。
签名就是要用来进行“验证”的数据,放在另一个地方就是要“隔离”出来,所以给这件事情起名为“隔离验证”也就再合理不过的了。通过上面的讲述,大家就应该能理解隔离验证的好处了,其好处的确是巨大的:交易是交易,签名是签名,对于交易来说,只关心 TxIn 和 TxOut 就好了,我们都清楚一个比特币交易其实就是花一笔之前收到的钱,也就是说要用之前收到的 TxOut 作为这一笔交易的 TxIn 然后发给后续的 TxOut,隔离之后的交易结构使得当您决定好花哪一笔钱、要花给谁的时候,交易的内容就已经是确定了的,交易的数据就不会再发生改变了,最终的 tx_id 也就是不变的了。想要验证签名?那就去 witness 里找到对应的签名数据验证一下合法性即可,又简单、有没有交易延展性的问题.
所谓的隔离验证,其实本质上不是为了解决块大小的问题而产生的改动,它也不是像一些人所说的那样“是 Core 团队中 BlockStream 公司的几名核心开发者的为了阻挠扩容而采取的阴谋”,而是比特币交易本来就应该是那个样子才对。隔离验证的结构改进,跟当年的压缩格式的公钥一样,都属于由于最初的结构设计的不合理而需要改进的内容。也就是说,比特币的交易,本来就应该是“隔离验证”的才对,只是由于最初中本聪设计成了当前的样子,所以一直才保留至今,如果当年就下了狠心给改好了,没准就没有今天的这么多争议了,比特币程序员们也能省下很多脑细胞用来干更多有意义的事情,而不是跟交易的结构做斗争,各类的比特币企业也能少受一些交易延展性的困扰。
隔离验证如何实现
由于要考虑到向下兼容、要考虑到各类客户端旧版本的兼容情况,所以开发者们希望尽可能用一种“软分叉”的方式来实现,为了做到这一点,当前一共提出了4个相关的 bip (141-144)。
我们都知道,对于任何一个计算机系统来说,新设计一套结构都是容易的,但想要做到向下兼容就要困难很多,想要让新老结构共存且旧版本还能正常运行就更加困难,尤其是当我们考虑到这些新旧结构的数据还要在同一个区块链上传播的情况,就需要进行更多的努力了。关于这些部分,在几个 bip 中进行了比较详细的描述,有兴趣的可进一步阅读相关技术文档:
比如说:
bip141 详细讲述了一个叫 “witness” 的新结构,脚本和签名等数据被移动到了这个新结构中,同时介绍了隔离验证如何做到的向下兼容等;
bip142 讲述了隔离验证的新的地址格式;
bip143 举例描述了隔离验证交易签名的验证方式;
bip144 讲述了如何在保持向下兼容的情况下节点间通报支持隔离验证的情况及传播和请求验证数据的方式;
好了,通过讲述这些内容,大家应该大致能明白隔离验证到底是怎么一回事了,隔离验证本身跟块大小并没有什么关系,只是由于验证数据处于兼容的目的被放在了另一块空间中,同时修改了块上限的计算方式使得隔离验证本身能起到一定程度的扩容作用,才让大家把它当成是另一种扩容方案,并且被当做是 2MB 方案的对立面。
最后,总结一下,隔离验证其实是对之前不太合理的比特币交易结构的一次优化,开发者们试图用一种影响尽可能小的、尽可能向下兼容的、“软分叉”的方式来实现,这种改进本身(就像是“压缩格式的公钥”一样)是非常合理的,与块大小之争没什么直接的关系。如果隔离验证的软分叉上线之后,我们可能会在相当长的一段时间里、在比特币网络上都能看到新旧版本的交易同时存在,就像直到今天,仍有人在用 5 开头的私钥(而不是压缩版本的 K 和 L 开头的私钥)一样。