术语表 /开发者参考
OP_CHECKSIG
- OP_CHECKSIG 是什么?
- OP_CHECKSIG 从堆栈中取出一个公钥和一个签名,并验证该签名确实对本笔交易作出了承诺,这是真正证明您拥有这些比特币的检查。
几乎每一笔被花掉的比特币都经过了这个操作码,即字节0xac。它根据附在签名末尾的 sighash 字节构建交易摘要,用公钥检查签名,然后压入真或假。如果压入的是假,这笔支出就不会发生,而这正是拥有比特币在代码中的全部含义。
运作方式
OP_CHECKSIG 弹出两个项目,先是公钥,其下方是签名,然后执行三项独立的工作,人们通常把它们混为一项。
首先,它确定签署的内容。签名的最后一个字节并不属于签名本身,而是一个标志,说明摘要涵盖了交易的哪些部分。绝大多数情况下的取值为0x01,表示涵盖所有输入和所有输出,因此签名之后对交易任何部分的改动都会使签名失效。构建该摘要的方法已被替换过两次:SegWit 0版支出由 BIP-143替换,Taproot 又由 BIP-341替换,两次都是为了避免哈希成本随交易大小的平方增长。
其次,它检查编码,而比特币在这方面异常严格。传统签名必须是有效的 DER 编码,包括标志字节在内最多72字节,且 S 值须位于曲线阶的下半部分。严格编码规则于2015年7月以 BIP-66的形式出现,切换当天下午就有六个区块成为孤块,原因是若干大型矿池在未经验证的区块头上继续构建区块。这仍然是挖矿与验证是两项不同工作的最清晰例证。
第三,它进行验证。传统支出和 SegWit 0版支出使用 secp256k1 上的 ECDSA。Tapscript 则改用 BIP-340规定的64字节 Schnorr 签名,要求检查失败时压入空项而不是其他内容,并且每检查一个签名,就从脚本的预算中扣除50个权重单位。
在哪里会遇到它
OP_CHECKSIG 为几乎所有现存的脚本收尾。传统输出以它结束,原生单密钥 SegWit 支出运行同样的模板和同样的最后一个操作码,尽管这些字节从未出现在链上,闪电网络的承诺脚本则在其时间锁分支和撤销分支之后以它结束。
查看区块浏览器时,这个例外值得了解。Taproot 密钥路径支出根本不运行任何脚本:节点直接用输出中的32字节密钥检查单个签名。既没有可显示的汇编,也没有可指向的操作码,这正是这种支出类型成本最低、隐私性最好的原因,也是一个只由其所有者花费的 bc1p 地址在链上看起来与其他所有地址完全相同的原因。
OP_CHECKSIG vs OP_CHECKSIGADD
OP_CHECKSIGADD 之所以存在,是因为 OP_CHECKSIG 无法计数。在多方脚本中,需要知道提交的签名里有多少个是有效的,而旧的设计用一个一次性吞下整个密钥和签名列表的操作码来解决这个问题。Tapscript 把它替换成一个只验证一个签名、并在堆栈上的累计总数中加一的操作码,于是2-of-3策略就变成一段以与2比较结束的简短序列。这一改变并非只是形式上的:逐个签名计数,才使得 Schnorr 签名可以批量验证,而批量验证正是 Taproot 验证速度的来源。