脚本校验器的开关板:一次软分叉在验证代码里拧开哪几颗螺丝 图 1
脚本校验器的开关板:一次软分叉在验证代码里拧开哪几颗螺丝 · 图 1

比特币脚本引擎校不校验某条规则,往往不取决于脚本本身,而取决于验证代码里一组叫”验证标志”的开关。同一笔交易,在某些区块高度被拒、在另一些高度被收,背后就是这些开关被逐个打开。看懂这组开关,才能看懂软分叉是怎样在节点里”生效”的。本文以比特币核心v31.0源码为准,讲清SCRIPT_VERIFY_*这套开关的结构、分工与打开时机。

开关板长什么样

脚本校验的入口函数除了接收脚本、栈和见证数据,还会接收一个位掩码参数——一组按位组合的标志。v31.0源码(src/script/interpreter.h)用一串枚举定义了它们:从SCRIPT_VERIFY_NONE(什么都不额外要求)开始,往后依次有SCRIPT_VERIFY_P2SH、_STRICTENC、_DERSIG、_LOW_S、_NULLDUMMY、_SIGPUSHONLY、_MINIMALDATA、_DISCOURAGE_UPGRADABLE_NOPS、_CLEANSTACK、_CHECKLOCKTIMEVERIFY、_CHECKSEQUENCEVERIFY、_WITNESS、_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM、_MINIMALIF、_NULLFAIL、_WITNESS_PUBKEYTYPE、_CONST_SCRIPTCODE、_TAPROOT、_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION、_DISCOURAGE_OP_SUCCESS、_DISCOURAGE_UPGRADABLE_PUBKEYTYPE,末尾用一个_END_MARKER收尾。源码里还有一句静态断言,要求这套标志的总数不超过63位——它被设计成一个64位整数的位域,每一位管一条规则,一次校验把当次该遵守的位全部传进来。

关键认知是:这些开关不是给运维拧的旋钮。用户没有哪个配置项能让节点”关掉_DERSIG”。开关的组合由更上层的代码按区块自动决定,脚本引擎只忠实执行”这一次要按哪些位来验”。

脚本校验器的开关板:一次软分叉在验证代码里拧开哪几颗螺丝 图 2
脚本校验器的开关板:一次软分叉在验证代码里拧开哪几颗螺丝 · 图 2

每个开关大致管哪一层

按来源可以把它们归成几组。最早一批把签名格式钉死:_STRICTENC要求公钥和哈希类型符合规范,_DERSIG要求签名是规范DER编码,_LOW_S要求S值取低半区,_NULLDUMMY要求多签的哑元素严格为空——这几条合起来掐掉了交易可延展性的几个著名缝隙,也都来自历史软分叉或事后修补。_MINIMALDATA、_SIGPUSHONLY、_CLEANSTACK偏向脚本形态的整洁性,堵住同值不同编码带来的歧义。_CHECKLOCKTIMEVERIFY和_CHECKSEQUENCEVERIFY把两个曾被禁用又复活的操作码变成可用能力。_WITNESS一族是隔离见证的开关:_WITNESS本身启用v0见证程序语义,_MINIMALIF、_NULLFAIL、_WITNESS_PUBKEYTYPE是随见证脚本一起加严的配套位,_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM则拦住”未来的版本”暂时别上链。_TAPROOT一族是最新一轮:启用v1见证与Schnorr校验,配套的_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION、_DISCOURAGE_OP_SUCCESS、_DISCOURAGE_UPGRADABLE_PUBKEYTYPE负责”把未来留白守住”。

这里有一对容易混的概念:硬拒与劝退。前一类标志直接把不合规的脚本判死;带DISCOURAGE_UPGRADABLE前缀的后一类,针对的是”当前版本不认识、但可能是未来升级”的结构——它不判共识无效,而是在标准性层面劝你节点先别接受。这种”先劝退、后升级”的两档设计,正是软分叉能反复安全进行的底层原因之一:老节点对新结构留了识别位,新节点对老规则留了执行位。

开关是怎么被打开的

标志的组装发生在区块校验层。v31.0源码(src/validation.cpp)在准备脚本环境时按区块高度查询各个部署项的状态:比如CSV部署生效之后才把SCRIPT_VERIFY_CHECKSEQUENCEVERIFY并进标志集,隔离见证与Taproot同理,各挂在自己的部署位上(DEPLOYMENT_CSV、DEPLOYMENT_SEGWIT、DEPLOYMENT_TAPROOT等)。BIP9的投票与激活在这里兑现成一行行按位或——所谓”软分叉在高度N生效”,落到代码,就是从高度N的区块起,验证者拿到的开关板上多亮了几颗灯。也正因为标志是按区块算出来的,全节点重放历史(reindex)时会用每个区块各自当时的标志集校验当时的交易,不会拿今天的规则去倒推历史——这是它能对着创世块一路重验到链尖的前提。

顺带解释两个常见现象。其一,同一笔带非规范DER签名的旧交易在2010年被收、在2015年被拒,不是链”变了心”,而是_DERSIG在中间某个高度开始亮着。其二,某些钱包软件自己维护一份”标准脚本”名单来决定要不要帮你广播,这份名单和节点标志集相近同源但并不等价——于是才有”节点其实会收、钱包却拒绝构建”的体验差。

对普通节点用户意味着什么

验证标志不是可选项,你唯一的选择是跑什么版本、在哪条链上跑。升级节点、别改源码里的部署参数、让节点按区块自己决定标志集,就是绝大多数人该有的全部姿势。真正会”拧开关”的是两类人:实现新协议的开发者在测试网里提前点亮尚未激活的标志演练,以及给钱包写标准性策略的工程师,他们必须逐条知道哪些位会在什么高度亮起,才能预判一笔交易在未来几周还能不能花得出去。

风险提示

本文描述的是共识验证代码的结构与机制,不构成投资建议,也不构成对任何具体交易能否被接收的保证。文中开关清单与打开时机描述以比特币核心v31.0源码为准,历史高度与参数可能随版本或分叉调整而变化,涉及生产决策时请以你实际运行版本的官方文档与源码为准。