签名哈希类型(SIGHASH)

数字签名适用于消息,在比特币中,这些消息就是交易本身。签名证明了签名者对特定交易数据的承诺。在最简单的形式中,签名适用于几乎整个交易,因此承诺了所有输入、输出和其他交易字段。然而,签名也可以仅适用于交易数据的子集,这对于许多情况都是有用的,正如我们将在本节中看到的那样。

比特币签名有一种方法来指示由私钥使用 SIGHASH 标志签名的交易数据的哪部分被包含在哈希中。SIGHASH 标志是附加到签名的单个字节。每个签名都有一个明确或隐含的 SIGHASH 标志,而且该标志可能从一个输入到另一个输入不同。一个包含三个已签名输入的交易可能有三个带有不同 SIGHASH 标志的签名,每个签名都签署(承诺)交易的不同部分。

请记住,每个输入可能包含一个或多个签名。因此,一个输入可能有具有不同 SIGHASH 标志的签名,这些签名承诺交易的不同部分。还要注意,比特币交易可能包含来自不同“所有者”的输入,这些所有者可能仅在部分构建的交易中签署一个输入,与其他人合作以收集所有必要的签名以使交易有效。 许多 SIGHASH 标志类型只有在考虑到多个参与者在比特币网络之外协作并更新部分签名的交易时才有意义。

SIGHASH 标志有三种类型:ALL、NONE 和 SINGLE,如表 8-1 所示。

表 8-1. SIGHASH 类型及其含义

SIGHASH标志描述

ALL

0x01

签名适用于所有的输入和输出

NONE

0x02

签名适用于所有的输入,但不适用于任何输出

SINGLE

0x03

签名适用于所有输入,但仅适用于与签名输入具有相同索引号的一个输出

此外,还有一个修饰标志,SIGHASH_ANYONECANPAY,可以与前面的每个标志结合使用。当设置了ANYONECANPAY时,只有一个输入被签名,其余的(及其序列号)则保持开放以进行修改。ANYONECANPAY的值为0x80,通过按位或运算应用于组合标志,如表8-2所示。

表8-2. 带有修饰符的SIGHASH 类型及其含义

SIGHASH标志描述

ALL|ANYONECANPAY

0x81

签名适用于一个输入和所有输出

NONE|ANYONECANPAY

0x82

签名适用于一个输入,但不适用于任何输出

SINGLE|ANYONECANPAY

0x83

签名适用于一个输入,以及具有相同索引号的输出

在签名和验证过程中应用SIGHASH标志的方式是,首先复制交易,然后将其中某些字段省略或截断(设置为空长度)。结果交易进行序列化。SIGHASH标志包含在序列化的交易数据中,然后对结果进行哈希。哈希摘要本身就是被签名的“消息”。根据使用的SIGHASH标志不同,将包括交易的不同部分。通过包含SIGHASH标志本身,签名也承诺了SIGHASH类型,因此它不能被更改(例如,由矿工更改)。

在“ECDSA签名的序列化(DER)”中,我们将看到DER编码签名的最后部分是01,这是ECDSA签名的SIGHASH_ALL标志。这将锁定交易数据,因此Alice的签名将承诺所有输入和输出的状态。这是最常见的签名形式。

现在让我们看看其他一些SIGHASH类型及其在实践中的使用方式:

ALL|ANYONECANPAY

这种类型的构建可用于创建“众筹”风格的交易。试图筹集资金的人可以构建一个只有一个输出的交易。单个输出支付“目标”金额给筹款人。这种交易显然是无效的,因为它没有输入。然而,其他人现在可以通过添加他们自己的输入作为捐款来修改它。他们使用ALL|ANYONECANPAY对自己的输入进行签名。除非收集到足够的输入达到输出值,否则该交易是无效的。每笔捐款都是一笔“承诺”,在筹集到整个目标金额之前,筹款人不能收取。不幸的是,该协议可以被筹款人添加他们自己的输入(或来自借给他们资金的人),即使他们没有达到指定的价值,也允许他们收取捐款。

NONE

这种类型的构建可用于创建特定金额的“持票支票”或“空白支票”。它承诺了所有的输入,但允许输出被更改。任何人都可以将自己的比特币地址写入输出脚本中。单独使用这种方式允许任何矿工更改输出目的地并将资金据为己有,但如果交易中的其他必要签名使用了SIGHASH_ALL或另一种承诺输出的类型,那么它允许这些花费者更改目的地,而不允许任何第三方(如矿工)修改输出。

NONE|ANYONECANPAY

这种类型的构建可用于构建一个“尘埃收集器”。在他们的钱包中有微小UTXO的用户,无法在费用超过UTXO价值时进行支出;请参阅“经济不划算的输出和不允许的尘埃”页。有了这种类型的签名,经济不划算的UTXO可以捐赠给任何人在他们想要的时候进行聚合和支出。

有一些提议修改或扩展SIGHASH系统。截至目前,最广泛讨论的提议是BIP118,该提议提议添加两个新的sighash标志。使用SIGHASH_ANYPREVOUT的签名不会承诺输入的outpoint字段,从而允许其用于花费特定witness程序的任何先前输出。例如,如果Alice收到了两个相同金额的输出到相同的witness程序(例如,需要她钱包的单个签名),则用于花费其中一个输出的SIGHASH_ANYPREVOUT签名可以被复制并用于花费另一个输出到相同的目标。

使用SIGHASH_ANYPREVOUTANYSCRIPT的签名不会承诺输出点、金额、见证程序或taproot merkle树(脚本树)中的特定叶子,因此可以花费任何之前的输出,只要签名可以满足条件。例如,如果Alice收到了两个不同金额和不同见证程序的输出(例如,一个需要单个签名,另一个需要她的签名加上一些其他数据),则用于花费其中一个输出的SIGHASH_ANYPREVOUTANYSCRIPT签名可以被复制并用于花费另一个输出到相同的目标(假设第二个输出的额外数据已知)。

两个SIGHASH_ANYPREVOUT操作码的主要预期用途是改进的支付通道,例如闪电网络(LN)中使用的通道,尽管还描述了几种其他用途。

在用户的钱包应用程序中,很少会看到SIGHASH标志作为选项呈现。简单的钱包应用程序使用SIGHASH_ALL标志进行签名。更复杂的应用程序,例如LN节点,可能使用替代的SIGHASH标志,但它们使用已经广泛审查的协议来理解替代标志的影响。

Last updated