超级详解隔离见证(segregation witness)

隔离见证(segretation witness)

起源

隔离见证由比特币BIP141提出,将比特币区块中交易的数据结构稍加修改,以解决如下问题:

  1. transaction malleability 问题。
  2. SPV证明中传输交易的签名成为可选
    隔离见证。
  3. 变相增加区块容量。

这里提到了一个概念,transaction malleabiity(交易延展性),有必要介绍一些背景知识。
在BIP141之前,比特币中一笔交易的数据结构如下:

[nVersion][txins][txouts][nLockTime]

nVersion表示交易的版本号,txins表示交易的输入,txouts表示交易的输出,nLockTime表示锁定时间,如果对交易数据结构感兴趣,可以参考比特币交易数据结构。比特币的txins中需要说明需要花费的是那一笔交易,比特币系统中对每一笔交易做两次sha256算法得到的32个字节的数据称之为txid,在txin中用txid来指明要花费的交易,同时还得提供相应的签名。比特币txins中具体的数据结构如下所示:

超级详解隔离见证(segregation witness)

scriptSig字段中需要提供签名和相应的公钥,一般形式为:signature public key的形式。
假如Alice向从Bob处购买了一件产品,向Bob发起了一笔交易TX,Alice的交易TX中会提供Alice的签名和公钥,假如Alice提供的公钥是完整的公钥形式(x,y),其中x =
F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB, 比特币系统中完整公钥的表示形式以04开头,因此在TX的scriptSig中公钥的形式如下:
04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE 52DDFE2E505BDB

比特币中公钥还可以用压缩公钥形式表示,压缩公钥中只需要提供x,以及y的奇偶性,根据椭圆曲线公式就可以计算出y,比特币系统用02x的压缩形式表示y是偶数,03x的压缩形表示y是奇数,假如Bob是恶意的商户,Bob可以把Alice的公钥修改为如下内容:
03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A,

假设Bob修改公钥后得到交易为TX‘,TX’仍然能够通过合法性验证,但是TX和TX‘两笔交易计算得到的txid却完全不同,如果TX’被记录在区块链上,那么TX就不能被记录在区块链上,Bob可以收到Alice的转账,但是Alice本人却无法发现交易已经上链,因为Alice搜索的时候根据TX的txid进行搜索,无法搜索到TX的信息。Alice发现转账失败,可能会再次向Bob转账,于是可能Alice连续支付两次。这种攻击方式就叫做transaction mallebility attack(交易延展性攻击)。

比特币中用transaction 的全部内容做两次哈希得到的txid来区分不同的交易,交易txin中ScriptSig的可修改性给了恶意节点可乘之机。为了解决这个问题,BIP141 中提出修改transaction 的数据结构,即在每个Transaction的末尾追加一个字段“witness”,专门用于存储ScriptSig中的签名和公钥数据,ScriptSig中不再存储数据。带有隔离见证的交易的数据结构如下:
[nVersion][marker][flag][txins][txouts][witness][nLockTime]

  • nVersion、txins、txouts、nLockTime的涵义和之前交易数据结构中的涵义相同。
  • marker是1字节的数据,而且必须是0x00。
  • flag必须是1字节的非空值,当前默认为0x01。
  • witness字段是单独存储交易中所有txin中的ScriptSig,而原来交易中txin中的ScriptSig不再存储任何数据。
    对新的交易进行哈希求txid时,不再包含witness内容部分,此时恶意节点对witness内容的修改不会影响到txid,因此就可以解决交易延展性问题。因为witness的出现,产生了另一种transaction ID,称做wtxID,即对包括witness在内的全部交易内容做哈希值,得到的称之为tx hash。如果是一个non-segwit transaction,因为根本没有witness data所以他的txid会和tx hash完全相同,但如果是segwit transaction的话,两者的值则会不同。

注意:不管transaction是不是segwit,input去reference到前一个output的时候都是用txID而不是tx hash。

另外,尽管segwit 巧妙地解决了transaction malleability 的问题,但事实上,经过了多次的patch,现行的Bitcoin 系统在没有使用segwit 的transaction 仍然不会遭受到transaction malleability 的攻击了。

既然计算txid的时候不包括witness部分的数据,那么如何保证区块数据的唯一性呢?在BIP141中提出了解决办法:
类似于对所有txid计算merkle root一样,对所有wtxid,也计算一个merkle root,并且把这个数据放在coinbase的输出锁定脚本中,这个锁定脚本至少38个字节,其中前6个字节必须是0x6a24aa21a9ed,其含义如下:

超级详解隔离见证(segregation witness)
如果在coinbase中有多个scriptPubKey匹配这个规则,则把输出索引最高的认为是wtxid的merkle root值。这笔交易就是一个示例.

Witness program

为了在一般transaction 的架构下能支持segwit,因此BIP141 就选择在前一笔output 的locking script 动点手脚,只要看到scriptPubKey 是0x00 开头,他就被赋予了新的意义。

P2WPKH

witness: <signature> <pubkey>
scriptSig: (empty)
scriptPubKey: 0 <20-byte-key-hash>
(0x0014{20-byte-key-hash})

全名是pay to witness public key hash,和原本的P2PKH 一样,需要有一个长度为20 bytes 的public key hash,用来之后验证signature。而当花费这个output 时,本来要放入input scriptSig 的signature 和public key 被放入到了witness program,因此input 的scriptSig 就可以是空的。简单来说,scriptPubKey 的开头为0 让script engine 知道这是一个segwit transaction.数字0是版本号,有了版本号,未来脚本升级就能更容易的向前兼容。
接下来的20 bytes 让script engine 更明确知道这是一个P2WPKH output,因此script engine 就会去witness program 拿signature 和public key ,最后的验证就和普通P2PKH 一样了。

P2WPKH的解锁脚本为空,而真正的解锁脚本内容被移到了原交易之外的witness部分。

P2WSH

witness: 0 <1 2 CHECKMULTISIG>
scriptSig: (empty)
scriptPubKey: 0 <32-byte-hash>
(0x0020{32-byte-hash})

全名是pay to witness script hash,和P2SH 很类似,witness program 装的内容基本上就是我们熟知的redeem script。和上面一样,scriptPubKey 的0 让script engine 知道这是一个segwit transaction,而接下来的32 bytes 让script engine 更明确知道这是一个P2WSH output,先透过验证witness program 的最后一个东西做sha256,要等于scriptPubKey的32-byte-hash,再来单独验证witness program 即可。

P2WPKH和P2WSH中分别用了不同的哈希函数,这是为了区分两种支付方式而做的设定。

隔离见证的兼容性

考虑两种场景:
(1)付款人的客户端支持隔离见证,而收款人不支持
(2)付款人的客户端不支持隔离见证,而收款人支持
对于第一种情况,如果收款人不支持隔离见证,那最终发布的地址将会是普通地址(P2PKH或P2SH),那所有交易按照原有的规则进行即可。
而对于第二种情况,聪明的社区开发者想出了一个过渡方案,即将P2WPKH或P2WSH植入P2SH。

P2WPKH植入P2SH

witness:
scriptSig: <0 <20-byte-public-key-hash>>
(0x160014{20-byte-key-hash})
scriptPubKey: HASH160 <20-byte-script-hash> EQUAL
(0xA914{ 20-byte-script-hash}87)

此处的脚本Hash值为RIPEMD160(SHA256(0 <20-byte-public-key-hash>))的结果,将该脚本Hash转换为P2SH地址,就是一个兼容segwit的地址,不支持隔离见证的客户端可以正常支付比特币给这种P2SH地址。

而对于支持隔离见证的客户端,仍可以将验证信息放在witness结构中,当然这种过渡方案的交易会较完全形态的方案稍大一些,但比无隔离见证的情况要小。

P2WSH 植入P2SH

witness: 0 <1 2 CHECKMULTISIG>
scriptSig: <0 <32-byte-hash>>
(0x220020{32-byte-hash})
scriptPubKey: HASH160 <20-byte-hash> EQUAL
(0xA914{20-byte-hash}87)

和上面一样,P2WSH 也可以包在P2SH 里面。因为这本质是一个P2SH output,所以segwit 的识别就移到了input scriptSig 去,而witness 的内容也和原生的P2WSH 相同。

隔离见证地址

当隔离见证被大范围接受后,钱包将开始使用一种新的专门针对Segwit原生的地址类型,这种地址将使用Base32编码,而不再使用Base58,即全部使用小写字母和数字表达。
如果你看到以bc1开头的地址,就是使用隔离见证地址进行的交易,这就是一个隔离见证的示例

第一笔隔离见证交易

支持隔离见证的交易在481824个区块开始正式**,
第一笔p2wpkh交易的id为:
dfcec48bb8491856c353306ab5febeb7e99e4d783eedf3de98f3ee0812b92bad
第一笔p2wsh交易的txid为:
461e8a4aa0a0e75c06602c505bd7aa06e7116ba5cd98fd6e046e8cbeb00379d6

隔离见证受到区块大小限制吗

有限制。
比特币的区块大小限制为1000000bytes,由于witness数据不包含在这个限制中,为了防止witness数据被滥用,仍然对总的区块大小做了限制。
引入了一个新概念叫块重量(Block weight)
Block weight = Base size * 3 + Total size
Base size是不包含witness数据的块大小
Total size是包含了witness数据的总大小
隔离见证限制Block weight <= 4000000。

引用