CHECKMULTISIG执行中的一些怪异情况

CHECKMULTISIG执行中存在一个奇怪的问题,需要做一些小的调整。当执行CHECKMULTISIG操作时,它应该消耗t + k + 2个堆栈项作为参数。然而,由于这个奇怪的问题,CHECKMULTISIG会多弹出一个值,或者比预期多一个值。

让我们通过之前的验证示例更详细地了解这个问题:

<Sig B> <Sig C> 2 <Pubkey A> <Pubkey B> <Pubkey C>3 OP_CHECKMULTISIG

首先,OP_CHECKMULTISIG弹出顶部项目,即k(在此示例中为“3”)。然后,它弹出k个项目,即可以签名的公钥;在此示例中,为公钥A、B和C。然后,它弹出一个项目,即t,即需要的签名数(即有多少个签名是必需的)。在这里t = 2。此时,OP_CHECKMULTISIG应该弹出最后的t个项目,即签名,并查看它们是否有效。然而,不幸的是,实现中的一个异常导致OP_CHECKMULTISIG弹出了比应该多一个项目(总共是t + 1)。这个额外的项目被称为虚拟堆栈元素,当检查签名时,它被忽略,因此对OP_CHECKMULTISIG本身没有直接影响。然而,虚拟元素必须存在,因为如果在OP_CHECKMULTISIG尝试在空堆栈上弹出时不存在,它将导致堆栈错误和脚本失败(将交易标记为无效)。由于虚拟元素被忽略,它可以是任何值。早期的惯例是使用OP_0,后来成为一个中继策略规则,并最终成为一个共识规则(通过BIP147的执行)。

由于弹出虚拟元素是共识规则的一部分,因此必须永远复制。因此,脚本应如下所示:

OP_0 <Sig B> <Sig C> 2 <Pubkey A> <Pubkey B> <Pubkey C> 3 OP_CHECKMULTISIG

因此,实际上在多签名中使用的输入脚本不是:

<Signature B> <Signature C>

而是:

OP_0 <Sig B> <Sig C>

一些人认为这种奇怪的现象是比特币原始代码中的一个错误,但还存在一个合理的替代解释。验证 t-of-k 签名可能需要比 t 或 k 更多的签名检查操作。让我们考虑一个简单的例子,1-of-5,具有以下组合脚本:

<dummy> <Sig4> 1 <key0> <key2> <key3> <key4> 5 OP_CHECKMULTISIG

首先对签名进行与 key0 的比对,然后是 key1,然后再对其前面的其他键进行比对,最后才与其对应的 key4 进行比对。这意味着即使只有一个签名,也需要执行五次签名检查操作。消除这种冗余的一种方法是为 OP_CHECKMULTISIG 提供一个映射,指示提供的签名与哪个公钥对应,从而使 OP_CHECKMULTISIG 操作只执行确切的 t 次签名检查操作。可能比特币的原始开发者在比特币的原始版本中添加了额外的元素(现在称为虚拟堆栈元素),以便他们可以在以后的软分叉中添加传递映射的功能。然而,该功能从未实现,并且2017年对共识规则的BIP147更新使得将来不可能添加该功能。

只有比特币的原始开发者能告诉我们虚拟堆栈元素是一个错误还是一个未来升级的计划。在本书中,我们只是将其称为奇异现象。

从现在开始,如果您看到一个多重签名脚本,您应该期望在开头看到一个额外的 OP_0,它唯一的目的是解决共识规则中的一个奇异现象。

Last updated