7.3 P2SH(Pay-to-Script-Hash)
支付脚本哈希P2SH是2012年推出的一种功能强大的新型交易,它大大简化了复杂交易脚本的使用。为了解释P2SH的必要性,让我们看一个实际的例子。
在【第1章 比特币介绍】中,我们曾介绍过迪拜的电子产品进口商Mohammed。他的公司账目广泛采用比特币的多重签名功能。多重签名脚本是比特币高级脚本最为常见的一种用途之一,是一种非常强大的功能。Mohammed的公司对所有客户付款,会计术语称为“应收账款”,即AR,都使用多重签名脚本。基于多重签名方案,客户支付的任何款项都会被锁定,必须至少两个签名才能解锁,一个来自Mohammed,另一个来自其合伙人或拥有备份密钥的律师。这样的多重签名机制能提升公司治理管控,同时也能有效防范盗窃、挪用和丢失。
最终的脚本非常长:
2 <Mohammed's Public Key> <Partner1 Public Key> <Partner2 Public Key> <Partner3 Public Key> <Attorney Public Key> 5 OP_C HECKMULTISIG
虽然多重签名十分强大,但使用起来还是多有不便。Mohammed必须在客户付款前将上面的脚本发送给每一位客户,而每一位客户也必须使用专用的能创建自定义交易脚本的比特币钱包软件,每位客户还得学会如何利用自定义脚本来创建交易。此外,由于脚本可能包含特别长的公钥,最终的交易脚本可能是最初交易脚本长度的5倍之多。超大的交易还将给客户造成费用负担。最后,这样一个大交易脚本将一直记录在所有节点内存的UTXO集中,直到该笔资金被使用。所有这些都使得这种复杂锁定脚本在实践中变得困难重重。
P2SH正是为了解决这一实际难题而被引入的,它使复杂脚本的使用能与直接向比特币地址支付一样简单。使用P2SH支付,复杂的锁定脚本被其电子指纹(加密哈希)所取代。当随后出现的一笔交易试图花费这个UTXO时,除了解锁脚本外,它还必须包含与哈希匹配的脚本。简单地说,P2SH意味着“支付给匹配这个哈希的脚本,这个脚本将在以后花费这个输出时呈现。”
在P2SH交易中,锁定脚本被哈希值取代,称为兑换脚本redeem script,因为它在兑换时提交给系统,而不是作为锁定脚本。表7-1显示了不带P2SH的脚本,表7-2显示用P2SH编码的相同脚本。
表7-1 不含P2SH的复杂脚本
Locking Script | 2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 CHECKMULTISIG |
---|---|
Unlocking Script | Sig1 Sig2 |
表7-2 P2SH复杂脚本
Redeem Script | 2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 CHECKMULTISIG |
---|---|
Locking Script | HASH160 <20-byte hash of redeem script> EQUAL |
Unlocking Script | Sig1 Sig2 |
从表中可以看出,对于P2SH,详细描述了花费输出条件的复杂脚本(兑换脚本)不会在锁定脚本中显示。相反,兑换脚本本身,锁定脚本中只出现了它的哈希值,在以后花费输出时才作为解锁脚本的一部分出现。这会把费用和复杂性的负担从交易发送者转移给接收者(承担支出)。
让我们再看下Mohammed公司的复杂的多重签名脚本和相应的P2SH脚本。
首先,看一下Mohammed公司使用的多重签名脚本,用于来自客户的所有付款:
2 <Mohammed's Public Key> <Partner1 Public Key> <Partner2 Public Key> <Partner3 Public Key> <Attorney Public Key> 5 CHECKMULTISIG
如果占位符由实际的公钥(以04开头的520字节)替代,你会看到脚本非常长:
2
04C16B8698A9ABF84250A7C3EA7EEDEF9897D1C8C6ADF47F06CF73370D74DCCA01CDCA79DCC5C395D7EEC6984D83F1F50C900A24DD47F569FD4193AF5DE762C58704A2192968D8655D6A935BEAF2CA23E3FB87A3495E7AF308EDF08DAC3C1FCBFC2C75B4B0F4D0B1B70CD2423657738C0C2B1D5CE65C97D78D0E34224858008E8B49047E63248B75DB7379BE9CDA8CE5751D16485F431E46117B9D0C1837C9D5737812F393DA7D4420D7E1A9162F0279CFC10F1E8E8F3020DECDBC3C0DD389D99779650421D65CBD7149B255382ED7F78E946580657EE6FDA162A187543A9D85BAAA93A4AB3A8F044DADA618D087227440645ABE8A35DA8C5B73997AD343BE5C2AFD94A5043752580AFA1ECED3C68D446BCAB69AC0BA7DF50D56231BE0AABF1FDEEC78A6A45E394BA29A1EDF518C022DD618DA774D207D137AAB59E0B000EB7ED238F4D800 5 CHECKMULTISIG
整个脚本都可由仅为20个字节的加密哈希所取代,首先采用SH256哈希算法,再对结果运用RIPEMD160算法。
命令行界面使用libbitcoin-explorer (bx)产生下面的脚本哈希:
echo \
2 \
[04C16B8698A9ABF84250A7C3EA7EEDEF9897D1C8C6ADF47F06CF73370D74DCCA01CDCA79DCC5C395D7EEC6984D83F1F50C900A24DD47F569FD4193AF5DE762C587] \
[04A2192968D8655D6A935BEAF2CA23E3FB87A3495E7AF308EDF08DAC3C1FCBFC2C75B4B0F4D0B1B70CD2423657738C0C2B1D5CE65C97D78D0E34224858008E8B49] \
[047E63248B75DB7379BE9CDA8CE5751D16485F431E46117B9D0C1837C9D5737812F393DA7D4420D7E1A9162F0279CFC10F1E8E8F3020DECDBC3C0DD389D9977965] \
[0421D65CBD7149B255382ED7F78E946580657EE6FDA162A187543A9D85BAAA93A4AB3A8F044DADA618D087227440645ABE8A35DA8C5B73997AD343BE5C2AFD94A5] \
[043752580AFA1ECED3C68D446BCAB69AC0BA7DF50D56231BE0AABF1FDEEC78A6A45E394BA29A1EDF518C022DD618DA774D207D137AAB59E0B000EB7ED238F4D800] \
5 CHECKMULTISIG \
| bx script-encode | bx sha256 | bx ripemd160
54c557e07dde5bb6cb791c7a540e0a4796f5e97e
上面的一系列命令首先将Mohammed的多签兑换脚本编码为一个序列化的十六进制编码比特币脚本。下面的bx命令使用RIPEMD160再次哈希,生成最终的脚本哈希:
Mohammed的兑换脚本20字节哈希:
54c557e07dde5bb6cb791c7a540e0a4796f5e97e
一笔P2SH交易把输出锁定在这个哈希,而不是那个特别长的兑换脚本。使用的锁定脚本为:
HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e EQUAL
正如你所看到的,这个脚本简短多了。P2SH交易等同于“支付给包含该哈希的脚本”,而不是“支付给5个多重签名脚本”。客户在向Mohammed公司支付时,只需在其支付指令中纳入这个非常简短的锁定脚本即可。当 Mohammed和他的合伙人想要花费这笔UTXO时,附上原始兑换脚本(他们的哈希锁定到UTXO的那个脚本)和必要的解锁签名即可,如:
<Sig1> <Sig2> <2 PK1 PK2 PK3 PK4 PK5 5 CHECKMULTISIG>
两个脚本经由两步实现组合。 首先,将兑换脚本与锁定脚本比对以确认其与哈希是否匹配:
<2 PK1 PK2 PK3 PK4 PK5 5 CHECKMULTISIG> HASH160 <redeem scriptHash> EQUAL
假如兑换脚本哈希匹配,解锁脚本自行执行以解锁兑换脚本:
<Sig1> <Sig2> 2 PK1 PK2 PK3 PK4 PK5 5 CHECKMULTISIG
本章中描述的几乎所有脚本只能以P2SH脚本来实现。 它们不能直接用在UTXO的锁定脚本中。
7.3.1 P2SH地址
P2SH的另一重要特征是它能将脚本哈希编码为一个地址,正如BIP-13中所定义的。P2SH地址是采用Base58Check对20个字节哈希的脚本进行编码,就像比特币地址是公钥20字节哈希的Base58Check编码一样。由于P2SH地址采用5作为前缀,这导致基于Base58编码的地址以“3”开头。
例如,Mohammed的复杂脚本,Base58Check编码后的P2SH地址为“39RF6JqABiHdYHkfChV6USGMe6Nsr66Gzw”。我们可以通过bx命令确认:
echo \
'54c557e07dde5bb6cb791c7a540e0a4796f5e97e'\
| bx address-encode -v 5
39RF6JqABiHdYHkfChV6USGMe6Nsr66Gzw
现在,Mohammed可以把这个地址发送给他的客户,他们可以采用任意比特币钱包进行简单支付,就像是比特币地址一样。前缀“3”暗示客户这是一种特殊类型的地址,一种对应于脚本而不是公钥的地址,但它作为付款方式与比特币地址完全相同。
P2SH地址隐藏了所有的复杂性,因此,运用其进行支付的人根本看不到脚本。
7.3.2 P2SH的优点
与在锁定输出中直接使用复杂脚本相比,P2SH特性提供了以下好处:
- 在交易输出中,复杂脚本由简短电子指纹取代,使得交易代码变短。
- 脚本被编译为地址,支付发送者及其比特币钱包都不需要复杂工程就可以执行P2SH。
- P2SH将构建脚本的负担转移至接收方,而非发送者。
- P2SH将长脚本数据的存储负担从输出方(既存储在区块链,又存储在UTXO集),转移至输入方(只存储在区块链中)。
- P2SH将长脚本数据的存储负担从当前(支付时)转移至未来(花费时)。
- P2SH将长脚本的交易费成本从发送方转移至接收方,接收方必须包含长的赎兑换脚本才能使用该笔资金。
7.3.3 兑换脚本和验证
在0.9.2版Bitcoin Core客户端之前,P2SH通过IsStandard(),仅限于标准类型的比特币交易脚本。这也意味着花费交易中的兑换脚本只能是标准化的P2PK、P2PKH或者多重签名。
0.9.2版的Bitcoin Core客户端,P2SH交易能包含任意有效的脚本,这使得P2SH标准更为灵活,可以用于多种新的或复杂类型的交易进行实验。
请记住不能将P2SH植入P2SH兑换脚本,因为P2SH规范不是递归的。虽然在技术上可以将RETURN【7.4 数据记录输出(RETURN操作符)】包含在兑换脚本中,规则中也未阻止此这样操作,但这没有实际用途,因为在验证期间执行RETURN将导致交易被标记为无效。
需要注意的是,因为在尝试使用P2SH输出之前,兑换脚本不会呈现给网络,因此,如果使用无效兑换脚本的哈希锁定输出,则不管如何都会进行处理。该UTXO将会被成功锁定,但是你将不能使用该笔资金,包含兑换脚本的花费交易也不被接受,因为该脚本是无效的。这样就会产生风险,你把比特币锁定在永不能花费的P2SH中。比特币网络本身会接受这个P2SH锁定脚本,即便它对应的是无效的兑换脚本,因为脚本哈希没有给出它所表示的脚本的含义。
注释 P2SH锁定脚本包含一个兑换脚本哈希,其中不包括该兑换脚本本身的任何线索。即便在兑换脚本无效的情况下,P2SH交易也会被认为有效并被接受。你可能会意外地锁死比特币,以后再也无法使用它。