7.2 多重签名

多重签名脚本设置了一个条件,脚本中记录了N个公钥,必须至少提供其中的M个签名才能解锁资金。这也称为M/N方案,其中N是密钥的总数,M是验证必须的签名数。例如,2/3的多重签名是三个公钥被列为潜在签名人,其中至少两个必须用于签名才能创建有效的使用资金的交易。

目前,标准多重签名脚本限制在最多3个公钥,这意味着可以执行从1/1到3/3或这个范围内的任意组合的多重签名。在本书出版时,可能会取消对3个公钥的限制,建议检查issstandard()函数,查看网络当前接受的值。注意,3个公钥限制只应用于标准多重签名脚本,不适用于P2SH脚本包装的多重签名脚本。P2SH多重签名脚本限制为15个密钥,允许最多15/15多重签名。我们将在【7.3 P2SH(Pay-to-Script-Hash)】学习P2SH。

设置M/N多重签名条件的锁定脚本的一般形式是:

  1. M <Public Key 1> <Public Key 2> ... <Public Key N> N CHECKMULTISIG

M是花费输出所需的签名的数量的底限,N是列出的公钥的总数。 设置2/3多重签名条件的锁定脚本如下所示:

  1. 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG

上述锁定脚本可被含有一对签名和公钥的解锁脚本满足:

  1. <Signature B> <Signature C>

或者由3个公钥中任意2个对应的私钥产生的签名组合。

这两个脚本一起形成下面的组合验证脚本:

  1. <Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG

执行时,仅当解锁脚本与锁定脚本设置的条件匹配时,此组合脚本的评估结果才为TRUE。上述例子中设置条件就是:解锁脚本是否含有3个公钥中的任意2个相对应的私钥的有效签名。

CHECKMULTISIG执行中的bug

CHECKMULTISIG的执行中出现了一个bug,需要做一些轻微的变通。 就是当CHECKMULTISIG执行时,它应该消耗堆栈上的M + N + 2个项目作为参数。 然而,由于该bug,CHECKMULTISIG会弹出一个额外的值或超出预期一个值。

我们使用前面的验证示例更详细地看一下:

  1. <Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG

首先,CHECKMULTISIG弹出最上面的项目,这是N(在这个例子中N是“3”)。然后它弹出N个项目,这是可以签名的公钥数。在这个例子中,是公钥A,B和C,然后,它弹出一个项目,即M,参与仲裁数目(需要多少个签名)。这里M = 2。此时,CHECKMULTISIG应弹出最后的M个项目,就是那些签名,并查看它们是否有效。然而,不幸的是,实施中的错误导致CHECKMULTISIG再弹出一个项目(总共M + 1个)。检查签名时,额外的项目被忽略,虽然它对CHECKMULTISIG本身没有直接影响。但是,必须存在额外的值,因为如果不存在,则当CHECKMULTISIG尝试弹出到空堆栈上时,会导致堆栈错误和脚本失败(将交易标记为无效)。因为额外的项目被忽略,它可以是任何东西,但通常使用0。

因为这个bug已经成为共识规则的一部分,所以现在它必须被永远复制。因此,正确的脚本验证将如下所示:

  1. 0 <Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG

这样解锁脚本就不是下面的:

  1. <Signature B> <Signature C>

而是:

  1. 0 <Signature B> <Signature C>

从现在开始,如果你看到一个多签解锁脚本,你应该期望开头就看到有一个额外的0,其目的是解决一个bug,却意外地成为共识规则。