概述
在“浅析比特币脚本”一文中,曾经提到过比特币脚本的一个应用是多重签名(Multi-Signature)。顾名思义,多重签名(以下简称多签)是指一笔交易需要多个私钥签名才能被执行。
在大多数情况下,交易都是一对一进行的,买家向卖家给出的地址(即卖家的公钥哈希)支付比特币,卖家要使用该比特币的时候提供自己的数字签名并验证自己的数字签名有效即可。但也会有这样一类场景:一个钱包被多个人共同持有。比如,某公司有5个合伙人,公司钱包相应就有5个私钥,每个合伙人持有其中1个私钥。为了防止其中某一个合伙人想中饱私囊或者肆意挥霍,必须有5个人中的3人同时签名才可以把钱包中的币取走,这类情况就需要使用“多重签名”。
在比特币中,多签的含义是:一个钱包如果有m个私钥,交易的时候,需要其中n(1≤n≤m) 个私钥的签名,交易才能被执行。对应到现实里,可以想象为这样一个场景:m个人共同管理一笔资金(比特币),他们各自持有一把私钥,要花费这笔资金需要取得其中至少n个人的同意(签名) 才能进行。对应到这个场景的话,m就是5,n就是3。
使用多签有这些优点:
- 一个钱包可以由多人共同管理
- 避免单点故障,防止某一个私钥被盗影响整个钱包
- 多个私钥可以起备份作用,防止单一私钥在丢失的情况下导致大量属于该私钥持有者的资金损失
多签主要有两种实现方式:
- CHECKMULTISIG实现多签
- P2SH实现多签
下面将对这两种实现方式进行介绍。
CHECKMULTISIG实现多签
用CHECKMULTISIG实现多签,输出脚本(也叫锁定脚本)的形式如下:
n
OP_PUSHDATA(publicKey_1)
OP_PUSHDATA(publicKey_2)
...
OP_PUSHDATA(publicKey_m)
m
OP_CHECKMULTSIG
如果输出脚本是这种形式的话,这笔交易就是一笔付款到多重签名(P2MS,Pay to Multi Signature)的交易,也常用multisig表示。其中m表示一共有m个人,每个人各自有一把公钥,共有m把公钥,这笔钱将会付给这m个人,由他们共同持有;n表示需要当这m个人想要使用这笔钱的时候,需要其中n个人的数字签名(即获得n个人的同意)才能使用这笔钱。
解锁UTXO(可理解为输出脚本所支付的比特币的ID)时,需要提供输入脚本(也叫解锁脚本),其形式如下:
OP_PUSHDATA(sig_1)
OP_PUSHDATA(sig_2)
...
OP_PUSHDATA(sig_n)
可以理解为需要n个签名(即获得n个人的同意)才能使用这笔钱。在需要使用这笔钱的时候,n个人提供输入脚本,将输入脚本与输出脚本拼接到一起,得到完整的执行脚本,然后执行该脚本。拼接后的脚本形式如下:
OP_PUSHDATA(sig_1)
OP_PUSHDATA(sig_2)
...
OP_PUSHDATA(sig_n)
n
OP_PUSHDATA(publicKey_1)
OP_PUSHDATA(publicKey_2)
...
OP_PUSHDATA(publicKey_m)
m
OP_CHECKMULTSIG
其执行过程如图:

可见,OP_CHECKMULTISIG一条语句就能完成整个多签的验证。
但是这种验证方式也有不足,它需要付款人(输出脚本)提供所有收款人的公钥。换句话说,要给有5个合伙人的公司打款,需要付款人先去获得每个合伙人的公钥,这样很不方便。
如果5个合伙人能提供单一的收款地址,不需要付款人把他们的公钥一一列出来,那样是最简便的,而这可以通过P2SH来实现。
P2SH实现单签
P2SH(Pay to Script Hash)是除P2K(Pay to public key)、P2KH(Pay to public key hash)之外的另一种付款方式。在P2SH的输入脚本中需要提供一个被称为RedeemScript的脚本,在P2SH的输出脚本则需要提供RedeemScript的哈希。在讨论P2SH实现多签之前,先讨论P2SH实现单签这种简单的情况。
P2SH的输出脚本格式如下:
OP_HASH160
OP_PUSHDATA(redeemScriptHash)
OP_EQUALVERIFY
输入脚本格式如下:
OP_PUSHDATA(sig)
OP_PUSHDATA(redeemScript)
拼接后的脚本如下:
OP_PUSHDATA(sig)
OP_PUSHDATA(redeemScript)
OP_HASH160
OP_PUSHDATA(redeemScriptHash)
OP_EQUALVERIFY
使用P2SH方式付款时,收款人只需要向付款人提供一个地址,付款人往这个地址支付比特币即可,这个地址就是收款人的Redeem Script的哈希;当收款人要花这笔钱的时候,需要提供相应的Redeem Script,以及收款人的数字签名。如果Redeem Script执行后,栈顶等于TRUE,那么表示整个付款的验证通过。
执行拼接后的脚本,执行过程如下图所示:

在上图中,如果OP_EQUALVERIFY执行结果为TRUE,那么接下来会执行redeemScript,而redeemScript是收款人自定义的,我们也可以通过它来实现多签。
P2SH实现多签
使用P2SH的支付方式,可以通过Redeem Script来实现多签。
P2SH实现多签时的输出脚本格式如下:
OP_HASH160
OP_PUSHDATA(redeemScriptHash)
OP_EQUALVERIFY
可以看到,P2SH的输出脚本格式在实现多签和单签时是一样的。
输入脚本格式如下:
OP_PUSHDATA(sig_1)
OP_PUSHDATA(sig_2)
...
OP_PUSHDATA(sig_n)
OP_PUSHDATA(redeemScript)
可以看到,在实现多签时,输入脚本与单签时的格式略微有所不同,此时需要提供的数字签名不再只有一个,而是多个。
拼接后的脚本如下:
OP_PUSHDATA(sig_1)
OP_PUSHDATA(sig_2)
...
OP_PUSHDATA(sig_n)
OP_PUSHDATA(redeemScript)
OP_HASH160
OP_PUSHDATA(redeemScriptHash)
OP_EQUALVERIFY
执行拼接后的脚本,如果OP_EQUALVERIFY等于TRUE,将开始执行redeemScript,redeemScript的格式如下:
n
OP_PUSHDATA(publicKey_1)
OP_PUSHDATA(publicKey_2)
...
OP_PUSHDATA(publicKey_m)
m
OP_CHECKMULTISIG
此时,执行redeemScript的过程将和前面"CHECKMULTISIG实现多签"一节的过程一样。
以上就是通过redeemScript来实现多签的过程,整个过程如下图所示。

参考文献
[1]比特币脚本是如何实现多签的?
[2]“付款到多重签名” 和 “付款到脚本哈希”
[3]多重签名
[4]Bitcoin and Cryptocurrency Technologies by Arvind Narayanan, Joseph Bonneau etl. Section 3.2