比特币交易签名如何验证?从Script到Taproot的验证原理
比特币交易签名验证,是节点在共识层确认“这笔花费是否由合法密钥授权”的核心机制。它从早期Script里的ECDSA校验,演进到SegWit的隔离见证,再到Taproot的Schnorr与脚本路径,目标始终是同一件事在不暴露多余信息的前提下,证明输入确实被允许花费。

比特币转账并不是提交一笔交易就能完成,节点还需要检查发送方是否有权花费对应的比特币。签名验证就是其中的关键环节:节点根据交易内容、解锁数据和对应的脚本规则进行检查,只有通过验证的交易,才有资格进入区块,随着比特币协议升级,签名验证也从传统的ECDSA逐步扩展到Taproot 使用的Schnorr签名。
签名验证在共识里管什么
一笔交易要花某个UTXO,必须提供解锁数据。节点把锁定脚本(或见证程序)和你给的签名、公钥放进栈里执行,最后栈顶为真才算通过,钱没有“账户密码”这一说,只有“能通过这段脚本”才有权动用输出。
常见锁定形式可以粗略对照
| 类型 | 典型脚本/程序 | 签名算法 | 见证位置 |
|---|---|---|---|
| P2PKH(遗留) | OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG | ECDSA (secp256k1) | scriptSig |
| P2WPKH(SegWit v0) | 见证程序0 + 20字节哈希 | ECDSA | witness |
| P2TR(Taproot) | 见证程序1 + 32字节x-only公钥 | Schnorr (BIP340) | witness |
2026年主网日常支付里P2TR与各类SegWit输出已占绝大多数新产出UTXO,老式P2PKH仍能花,只是费率与可塑性都更差。
Legacy Script:CHECKSIG如何判真
CHECKSIG就是在检查:“这个人是不是真的有权利花这笔钱?”,当有人想花一笔锁定在某个公钥哈希上的比特币时,大致要过这几关:
1、证明公钥是对的
拿出自己的公钥,计算它的HASH160,看是否和输出脚本里锁定的哈希一致。对得上说明这把公钥就是收款时指定的那把。
2、生成要验证的消息
按照签名时约定的规则(sighash类型),把交易的相关部分序列化,再做两次SHA256,得到一个32字节的摘要。
3、验证签名
用ECDSA算法检查签名里的 (r, s) 是否能和这把公钥、以及上面算出的摘要对上。
4、结果
验证通过,CHECKSIG就在栈上留下true(真),交易才有机会继续往下走。
sighash是什么?
sighash决定了“签名到底承诺了交易的哪些部分”。
1、SIGHASH_ALL:(最常用):把所有输入和输出都签进去,最安全。
2、SIGHASH_SINGLE:、SIGHASH_NONE,以及配合ANYONECANPAY的组合:可以只签部分内容,多签协作或预签交易时会用到。
节点验证时,必须用和签名者完全一样的规则重新计算摘要,差一个字节都会验证失败。
早期的副作用
以前签名是放在scriptSig里的,而scriptSig又会参与TxID的计算。
结果就导致,有人可以稍微改一下签名的编码方式,交易的实际内容没变,但TxID却变了(这就是交易延展性),这个问题后来直接推动了隔离见证(SegWit)的出现,把签名移到见证区,不再影响TxID。
SegWit:见证离开txid
●SegWit(主网2017年激活)把签名挪到witness字段,txid不再包含见证,wtxid才覆盖完整数据。对P2WPKH,解锁就是witness里的signature + pubkey,逻辑仍是ECDSA + 公钥哈希比对,只是程序从“完整脚本”收成了版本号加哈希的短见证程序。
●P2WSH则把整段赎回脚本的SHA256放进输出,花费时出示脚本原文和满足它的见证。多签、时间锁、相对锁高(CSV)这类条件,在v0见证里已经很常见。
●验证时节点要检查权重单位(weight)见证数据按1/4折扣计入区块权重,同样签名字节,SegWit通常比遗留交易更省手续费。
Taproot:键路径与脚本路径
Taproot(2021年11月激活,BIP340/341/342)把默认路径收成一次Schnorr签名,复杂脚本藏进默克尔树,需要时才揭开。

键路径花费(Key path)
输出是一个32字节的x-only公钥Q,平常协作方预先把内部公钥P与脚本树根M调成
Q = P + H(P || M) · G(概念上,实际按BIP341的tagged hash计算)。
大家都同意直接花时,只对Q做一次BIP340 Schnorr签名,witness里基本就是那64字节签名(可带sighash类型字节),链上外人看见的就是普通单签,分不清背后有没有多签或条件树。Schnorr还支持原生批量验证,全节点同步时能把多笔签名摊在一起做,吞吐更友好。
脚本路径花费(Script path)
谈不拢、要走分支时,花费者出示
● 叶子上的Tapscript。
● 控制块(control block):内部公钥、叶子版本、以及到根的默克尔证明。
● 满足该脚本所需的数据(签名等)。
节点用控制块重算默克尔根,确认它和内部公钥能导出输出里的Q,再执行Tapscript。Tapscript里的OP_CHECKSIG换成了Schnorr语义,并引入OP_CHECKSIGADD方便多签计数,老的OP_CHECKMULTISIG不再沿用同一套方式。
从摘要到算法:两次“签字”区别在哪
不管Script还是Taproot,流程都是“先承诺交易内容,再验密码学签名”,差别在承诺字段和曲线算法。
| 对比项 | ECDSA(遗留 / SegWit v0) | Schnorr(Taproot) |
|---|---|---|
| 公钥形式 | 压缩/未压缩,验证时还原 | x-only 32字节 |
| 签名长度 | DER可变,约70–72字节量级 | 固定64字节(外加可选类型) |
| 线性同态 | 弱,聚合别扭 | 天然可聚合、易批量验 |
| 链上外观 | 多签与单签脚本不同 | 键路径下多签可与单签无区别 |
Taproot的sighash也重新定义过(默认行为更接近“签进所花输出与相关输入”),并规定了tagged hash,避免跨协议撞摘要,验证库若混用旧序列化,会直接验签失败——钱包与节点必须同一套BIP规则。
实际会碰到的场景
日常单地址转账:现代钱包多默认生成Taproot或原生SegWit地址。你在手机上滑一下确认,本地用种子派生私钥,按键路径签名,广播后节点走Schnorr校验。
交易所热钱包与批量提现:大量输入时,Schnorr批量验证能降低节点CPU峰值,手续费估算仍按weight,witness折扣还在。
多签与协作托管:MuSig2一类协议可在链下聚合成一把Taproot外部密钥,成功路径完全走key path,只有争议才打开脚本树里的超时退款叶。链上字节和隐私都优于老式n-of-m裸多签。
时间锁与合约:CSV/CLTV仍可写在Tapscript叶子里,闪电网络等二层的惩罚与撤销路径可以藏在树中,平常通道更新尽量保持键路径外观。

验证边界与常见误区
节点只保证脚本规则与签名数学成立,不保证“地址背后的人是你以为的那个人”。钓鱼地址、错误网络(主网/测试网)、费用过低卡内存池,都不在签名算法的责任范围里。
此外几条容易踩坑
● 同一私钥,遗留ECDSA与Taproot Schnorr的消息承诺不同,不能把两种签名当一回事混用。
● 脚本路径必须提供完整且正确的控制块,少一节默克尔节点就会在“能否导出Q”这一步失败。
● miniscript / 描述符钱包能降低手写脚本出错概率,但最终上链的仍是展开后的Tapscript或旧脚本,节点不认“上层策略语言”。
● 量子计算若未来威胁椭圆曲线,现有ECDSA/Schnorr都在同一大类风险里,社区讨论的后量子方向属于更远期软分叉议题,2026年主网验证规则仍是secp256k1。
想对照规范原文,可查Bitcoin改进提案BIP340(Schnorr)、BIP341(Taproot输出与花费)、BIP342(Tapscript),以及比特币核心客户端对VerifyWitnessProgram、CheckSig相关实现。测试可在signet或regtest上重放,不必碰主网真金。
常见疑问
为什么我的钱包显示“Taproot手续费更低”,验证规则变简单了吗?
不是规则变松,是键路径下见证更短,按权重计费时字节更少,节点验Schnorr也常能批量做。复杂脚本一旦走脚本路径,控制块和叶子都会占权重,未必比SegWit多签更便宜,要按叶子深度和脚本长度算。
老地址里的币要迁到Taproot才安全吗?
安全模型没有因为“还在P2PKH”就突然坏掉,该用的私钥强度与防钓鱼习惯一样重要。迁移主要是为了费用、隐私外观和后续合约能力,搬迁本身需要一次合法签名花费,照样走本文说的验证流程。
节点会不会只抽查签名?
全验证节点对每个输入的每条脚本路径都会执行到结果为真(或在Taproot键路径上完成一次Schnorr校验)。轻客户端靠区块头与默克尔证明,信任模型不同,但矿工打包进块的交易,仍须先过完整验证规则。






