一个比特币脚本数字最多五个字节:CScriptNum 与脚本的编码自律 图 1
一个比特币脚本数字最多五个字节:CScriptNum 与脚本的编码自律 · 图 1

脚本世界里的数字怎么摆

比特币脚本是一台栈机:操作数与操作码交替压栈,数字本身以字节串形式存在。为了让同一数值只有一个合法摆法,规则要求最小化编码——用尽可能少的字节表示数值,不允许多余的前导零;符号位单独占用最高有效字节的符号位(这也是编码里有负零这种形态的原因)。数字直接进脚本的合法长度上限是十一字节。而多数算术类操作码在执行前还有一道更严的检查:操作数必须能放进四字节(并带符号),放不下就整笔脚本失败,默认阈值在源码常量 nMaxScriptNumLen 里写着四。

于是脚本数字身上有两道闸门:编码时十一字节封顶,用做算术时四字节封顶。它们不是互相矛盾的规格,而是各司其职——前者管交易结构的瘦,后者管算术语义的稳。

一个比特币脚本数字最多五个字节:CScriptNum 与脚本的编码自律 图 2
一个比特币脚本数字最多五个字节:CScriptNum 与脚本的编码自律 · 图 2

五百年前的算术边界

四字节上限换成人话,是脚本里的加减比较只在大约正负二十一亿这个区间内可靠工作。这个量级对比特币的绝大多数用法绰绰有余:金额比较在时间锁与金额锁里出现,但那些脚本消费的是高度、序号与标志位;对聪级别的金额做算术,本身也不是脚本层设计的目标。限制真正的价值在于给确定性买保险:脚本执行要在几万个节点上逐字节一致地复现,一个无限精度大数运算的定义分歧(进位、舍入、溢出行为)就是一颗潜伏的共识炸弹。把运算数的宽度锁死在四字节,等于把所有实现的算术语义压进同一条轨道——这是节点软件用可维护性换共识确定性的典型取舍。

超界的直接表现是明确的脚本失败。历史上这类报错常被误解成“脚本太复杂”,实际多数只是编码触到了闸门:要么数字带着多余前导零进来,要么某段被当数字消费的数据实际长度超过阈值。排障时把“数据被解释成数字”这一句记住就够了:一个脚本能否通过,取决于操作码如何解释栈上元素,而不是数据本身长什么样。同一段字节,在比较语境里是数字、在哈希预镜像里只是原材料——报错文本往往不会替你区分这一层。

OP_0 到 OP_PUSHNUM16:一条省字节的走廊

脚本给零到十六这个小区间留了一条捷径:OP_0(脚本语境里也称 OP_FALSE)与 OP_PUSHNUM1OP_PUSHNUM16 这一族操作码,每个操作码只占一字节,压栈却展开成对应数值的编码(OP_0 展开是空串,OP_PUSHNUM16 展开是单字节十六)。对高频出现的数字——比如多重签名的门限参数——这条走廊每处省下约一个字节。脚本空间按字节计价的世界(区块权重、每千字节的费率),一个字节也是成本。顺带一个常见困惑:这段操作码的名字在代码里经历过从 OP_1OP_PUSHNUM1 一类的更名,为的是把它们和“按数值压栈”的动作更明确地区分开;读旧资料时遇到两套叫法,知道是同一族即可。

规则本身是软的,代价才是硬的。一个实现若对最小化睁一只眼闭一只眼,它产出的交易在别人节点上直接判非法——没有协商,没有警告,就是花不出去。这正是脚本规则“窄进宽出”哲学的缩影:生产者必须严格,验证者对结构严格、对语义宽容(只要符合规则,脚本可以组合出设计者没见过的用法)。理解这一点,也就理解了为什么比特币脚本被反复强调“简单”:简单不是能力缺陷,是可验证性的存款。

快速问答

问:脚本里能比较一千万聪这种数字吗? 答:可以。一千万在一亿零一之内,是标准四字节能装下的量;脚本限制的是数值宽度,不是金额场景。

问:为什么我的脚本用了 OP_ADD 就失败? 答:先查操作数编码——多余前导零、或被当数字的长数据都可能触发最小化与长度检查,报错未必说明是哪一样。

问:OP_0 和数字零等价吗? 答:作为压栈结果等价;作为脚本里的占位符,一字节的操作码写法比零字节的数据写法在结构上更清晰,两者都合法。

风险提示:自定义脚本构造不当会导致资金永久无法花费,请在测试网充分验证后再用于主网;本文不构成投资建议。