第一性原理 / 自托管与安全

多签:从脚本原语到托管实践

2026-10-26 · 第一性原理 · 原创

自托管的定义是"能签名者即持有者",但它没有规定签名者必须是一个人、一把钥匙。多重签名(multisig,下称多签)把花费条件从"一把私钥"改成"N 把公钥中任意 M 把私钥的签名"(M-of-N),从而把单点故障与单点作恶同时移出系统。它是比特币脚本最早被标准化的复合条件,也是今天机构托管与协作托管的技术地基。本文只梳理技术与事实脉络:原语如何定义、为何最初不好用、P2SH 如何改变这一点、一个著名的历史 bug 如何被软分叉收束,以及 Schnorr 时代之后多签的形态变化。

事实一:原语——OP_CHECKMULTISIG

比特币脚本自创世版本起就包含 OP_CHECKMULTISIG 与 OP_CHECKMULTISIGVERIFY 两个操作码。写成脚本模板即:

M <公钥1> <公钥2> … <公钥N> N OP_CHECKMULTISIG

执行时,它按顺序用栈上的签名逐一匹配公钥(签名顺序必须与公钥顺序一致),凑满 M 个有效签名即通过。这个原语有两个与生俱来的约束。其一是规模:受脚本元素 520 字节等共识限制约束,裸多签的 N 在共识上限上至多 20,实际可用规模还受交易大小与签名运算计数限制。其二是本文下一节的主角——它多弹出一个栈元素。

**那个多余的栈元素。**原始实现中 OP_CHECKMULTISIG 会比应有的多消费一个栈元素(off-by-one)。由于改动该行为会使既有脚本全部失效,这个"bug"被永久保留为协议事实:所有多签的解锁脚本都必须在签名之前多压入一个占位元素,通常是 OP_0(空值)。今天任何一笔多签花费的开头那个 0,都是 2009 年一行代码留下的化石。

事实二:BIP-11 把 M-of-N 定为标准交易

原语存在不等于钱包会用。2011 年 10 月 18 日,Gavin Andresen 提出 BIP-11(M-of-N Standard Transactions),把"M-of-N 多签输出"定义为一种标准交易类型:节点默认中继、矿工默认打包。其动机写得很直白:让需要多个签名才能动用的钱包成为可能——例如用户与"钱包保护服务"各持一钥的 2-of-2,或者托管与托管方的分权安排。BIP-11 同时给裸多签设下实用的标准性边界(标准中继的裸多签以小规模为限)。在此之后,多签从"脚本里能写"变成"网络里能跑"。

但裸多签(bare multisig)有一个结构性缺陷:全部 N 个公钥都直接写在输出脚本里。这意味着收款方要把一长串公钥编码成地址交给付款方,输出体积大、费用由收款结构的复杂度决定,而且公钥集合在收款时就完全暴露。付款方需要为收款方的复杂条件买单——这在支付逻辑上是倒置的。

事实三:BIP-16(P2SH)把复杂度移回给花费方

BIP-16(Pay to Script Hash)给出的解法是把脚本哈希化。输出不再写完整脚本,只写赎回脚本(redeem script)的 HASH160 哈希(地址格式由 BIP-13 定义,即以 “3” 开头的地址);花费时再揭示赎回脚本并提供签名,节点验证"脚本哈希相符且脚本执行通过"两步。对多签而言,赎回脚本正是 M <公钥…> N OP_CHECKMULTISIG。

P2SH 在 2012 年 4 月 1 日经软分叉激活(见软分叉历史)。它的效果超出多签本身:一切复杂花费条件自此共享同一种"短地址、复杂度后置"的形态,付款方只需面对 20 字节哈希。SegWit 时代这一模式被原样推广:多签脚本可以放进 P2WSH(见证脚本哈希)中,此时赎回脚本的哈希与签名都进入权重打折的见证区,规模上限也相应放大,3-of-5、5-of-7 这类配置的链上成本显著低于裸多签时代。多签今日的默认形态——P2SH 或 P2WSH 包裹的多签脚本——即由此定型。

事实四:BIP-147 给化石套上缰绳

多余栈元素不只是占位问题:既然那个元素可以是任意值,任何中继者都能替换它而不使交易失效——这构成一个交易延展性(malleability)向量,会改变交易标识并干扰未确认交易链。BIP-147(Dealing with dummy stack element malleability)的处理方式不是修掉 bug,而是给它立规矩:作为共识规则(NULLDUMMY),要求该占位元素必须为空字节数组,否则脚本直接失败。BIP-147 随 SegWit 软分叉一并部署(2017 年 8 月激活):对见证脚本内的多签即时生效。自此,那个位置上的值被锁死为 0,中继者无从下手。这是比特币处理历史包袱的典型手法——不能改的,就围起来管住。

转折:Schnorr 与 Taproot 改变了多签的"形状"

2021 年 11 月 14 日,Taproot 在高度 709,632 激活,BIP-340 把 Schnorr 签名引入共识。Schnorr 的线性性质使密钥聚合成为可能:多方可以把各自的公钥合成一把聚合公钥,链上只出现这一把公钥和一个 64 字节签名,看起来与单签无异。多签由此分化出第二条技术路线——不再靠脚本逐一出示公钥与签名,而是靠密码学在链下聚合。

谱系中的三个位置需要分清:

三者不是替代关系,而是同一需求(消除单点)在不同层的实现:共识脚本层、聚合协议层、门限协议层。选择哪一层,取决于使用方更看重链上可审计性,还是链上不可分辨性。

实践形态:托管与协作托管

落到托管实践,多签的配置收敛为少数几种反复出现的形态,背后的逻辑都是同一条:让任何单一主体——包括被盗、被胁迫、作恶或消失——都不足以单独移动资金。

机构自托管的金库常用 2-of-3 或 3-of-5:密钥分置于不同地点、不同介质与不同负责人,日常支出需要跨地点的协同签名,单点失陷不导致失金,单把密钥丢失也不导致锁死(3-of-5 容忍两把丢失)。交易所与托管商的冷钱包长期采用同类结构,区别只在签名流程的组织化程度。

个人与中小主体则更多使用协作托管:用户自持两把、服务商持一把,构成 2-of-3。用户日常花费自签两把即可;服务商的那一把只在用户丢失一把、需要恢复时才参与联签,服务商单独不能动用资金,其角色被脚本限定为"备份签名者"而非共管人。这正是 BIP-11 在 2011 年写下的"钱包保护服务"构想的当代形态——十余年间变的只是钱包软件与硬件签名设备,不变的是脚本模板本身。

与这两种形态相对照的是单签托管(把币交给交易所):它把全部信任压回一个主体,恰好回到多签想要消除的单点。多签在托管谱系中的位置因此可以一句话概括:它让"托管"从一个信任问题,变成了一个密钥分布与签名流程的工程问题。

多签的历史给比特币的保守主义提供了一个完整的样本:一个原语(2009)、一份标准化提案(2011)、一次改变可用性的软分叉(2012)、一次给历史 bug 立规矩的软分叉(2017)、一次换掉签名算法后长出的新分支(2021)。每一步都不推翻前一步:今天的 P2WSH 多签里,仍然躺着 2009 年的那个占位 0。这种"只围不改"的演进方式看起来笨拙,却是它能承载真实资金十五年的原因——在比特币里,能活下来的设计,都是那些不需要后人擦屁股推倒重来的设计。


事实来源:bitcoin/bips 仓库的 BIP-11、BIP-13、BIP-16、BIP-147、BIP-327、BIP-340 原文(本站均有解读页);Bitcoin Wiki 的 OP_CHECKMULTISIG 条目;Bitcoin Core 源码中脚本解释器对多签与 NULLDUMMY 的实现。观点部分为本站分析。