支付到脚本哈希(Pay to Script Hash,P2SH)

支付到脚本哈希(Pay to Script Hash,P2SH)于2012年引入,作为一种强大的新型操作,极大地简化了复杂脚本的使用。为了解释P2SH的必要性,让我们看一个实际的例子。

穆罕默德是一家总部位于迪拜的电子产品进口商。穆罕默德的公司广泛使用比特币的多重签名功能来管理其企业账户。多重签名脚本是比特币高级脚本功能中最常见的用法之一,也是一种非常强大的功能。穆罕默德的公司为所有客户支付使用了多重签名脚本。客户支付的任何款项都被锁定,需要至少两个签名才能释放。穆罕默德、他的三位合伙人和他们的律师每人都可以提供一个签名。这样的多重签名方案提供了企业治理控制,并防止了盗窃、侵占或丢失。

由此产生的脚本相当长,看起来像这样:

2 <Mohammed's Public Key> <Partner1 Public Key> <Ppartner2 Public Key> <Partner3 Public Key> <Attorney Public Key> 5 OP_CHECKMULTISIG

虽然多重签名脚本是一种强大的功能,但它们使用起来很麻烦。考虑到前面的脚本,穆罕默德必须在支付之前将该脚本传达给每个客户。每个客户都必须使用具有创建自定义交易脚本能力的特殊比特币钱包软件。此外,由于该脚本包含非常长的公钥,因此生成的交易将比简单的支付交易大约多五倍。额外的数据负担将以额外的交易费用的形式由客户承担。最后,像这样的大型交易脚本将在每个完整节点的UTXO集中保存,直到被花费。所有这些问题使得在实践中使用复杂的输出脚本变得困难。

P2SH的开发旨在解决这些实际困难,并使使用复杂脚本像向单个密钥比特币地址支付一样简单。在P2SH支付中,复杂脚本被替换为一个承诺,即加密哈希的摘要。当稍后尝试花费UTXO的交易被提交时,它必须包含与承诺匹配的脚本,以及满足脚本的数据。简单地说,P2SH意味着“支付到与此哈希匹配的脚本,一个将在此输出被花费时稍后呈现的脚本”。

在P2SH交易中,被哈希替换的脚本被称为赎回脚本,因为它是在赎回时向系统呈现的,而不是作为输出脚本。表7-1显示了不使用P2SH的脚本,表7-2显示了使用P2SH编码的相同脚本。

表 7-1. 不使用P2SH的复杂脚本

Output script
2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 OP_CHECKMULTISIG

Input script

Sig1 Sig2

表 7-2. 使用P2SH的复杂脚本

Redeem script
2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 OP_CHECKMULTISIG

Output script

OP_HASH160 <20-byte hash of redeem script> OP_EQUAL

Input 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 OP_CHECKMULTISIG

整个脚本可以通过首先应用 SHA256 哈希算法,然后在结果上应用 RIPEMD-160 算法来表示为一个 20 字节的加密哈希。例如,以 Mohammed 的赎回脚本的哈希值为起点:

54c557e07dde5bb6cb791c7a540e0a4796f5e97e

一个 P2SH 交易将输出锁定到此哈希值,而不是较长的赎回脚本,使用了一个特殊的输出脚本模板:

OP_HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e OP_EQUAL

正如您所看到的,这个输出脚本要短得多。与“支付给这个 5 个密钥的多重签名脚本”相比,P2SH 等效交易是“支付给一个具有此哈希的脚本”。客户向 Mohammed 的公司付款时只需在付款中包含这个更短的输出脚本。当 Mohammed 和他的合作伙伴想要花费这个 UTXO 时,他们必须呈现原始的赎回脚本(锁定该 UTXO 的哈希)以及解锁它所需的签名,就像这样:

<Sig1> <Sig2> <2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG>

两个脚本在两个阶段进行合并。首先,检查赎回脚本与输出脚本是否匹配:

<2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG> OP_HASH160 OP_EQUAL

如果赎回脚本的哈希值匹配,则执行赎回脚本:

<Sig1> <Sig2> 2 <PK1> <PK2> <PK3> <PK4> <PK5> 5 OP_CHECKMULTISIG\

Last updated