比特币脚本没有寄存器,没有变量,只有一个栈:解锁脚本先把数据压上去,锁止脚本再消费它们。既然是栈,就要防”无限堆”——一千个元素,就是共识规则给这台堆叠机设的高度天花板。这个数字平时几乎无人提起,但它是脚本资源账本里最底层的一格。本文按 v31.0 源码讲清 MAX_STACK_SIZE 的数值、检查点,以及它与几道更矮的”标准性”闸门的层级关系。
数值与两处检查
共识常量在 src/script/script.h:栈深上限定义为一千。解释器在两个位置执法。第一处在脚本执行途中:每当一条控制流指令要开新分支,执行栈会配一套备用栈暂存不活动分支的状态,源码在推送任何元素前都要求主栈与备用栈的元素数合计不得超过上限——换句话说,分支嵌套的暂存开销同样占这份一千格的额度。第二处在执行收场时:无论走的是传统脚本还是 Tapscript 路径,脚本结束时留在栈上的元素数一旦超过上限,同样判失败,错误标记指向”栈超限”这一类。
配合另一条单元素尺寸上限(五百二十字节),这两层规则合起来把一枚脚本实例能占用的临时内存钉死在一个不大的量级内:栈机不怕算得慢,怕的是无界增长吃掉验证节点的内存。
为什么平时见不到它
普通钱包交易的栈离一千格很远:一次典型的 Pay-to-Witness-Public-Key-Hash 花费只压入签名与公钥两三个元素。真正逼近上限的构造出现在两类场景:大嵌套的条件脚本在分支切换时让备用栈持续堆叠;以及多签解锁把大量签名与公钥同时压上栈——不过这类构造通常先撞上的不是共识栈限,而是更矮的标准性闸门。
三道高度不同的闸
要把”交易被节点拒绝”这类事归对因,得区分三层。最松的是共识层:栈超一千、脚本超一万字节、单元素超五百二十字节,触线即无效,全网无人接受。中间是标准性层:隔离见证脚本的解锁栈最多一百项、每项最多八十字节、脚本本体最多三千六百字节——超了不算违规,但默认配置的节点与矿工视其为非标准,事实上不收。最外圈才是各节点自设的政策。一笔”看起来合法却被拒绝”的复杂脚本,报错落在哪一层,决定了你该改结构、改签名方案,还是只需说服某个矿工打包。
顺带一提:Miniscript 这类脚本编译器会在生成阶段自查执行栈深是否超过一千,把资源违规消灭在出生前——这也是”写脚本之前先算资源账”这条经验的产品化形态。
对排障的意义
当验证类错误指向栈尺寸时,方向不是调节点参数,而是重构脚本:减少分支嵌套深度、拆分多签、改用 Tapscript 的密钥树把大脚本折叠成小分支。共识层的数字没有配置可改——那是一千格与所有全节点之间的约定。
一千格账本的消耗方式
以典型结构直观感受一下量级:一枚多签解锁里每个签名与每把公钥各占一格,十五把钥匙的多签光解锁数据就要占掉三十格;脚本里每层活动分支在备用栈上留着各自的旧状态,嵌套越深,瞬时峰值越高;哈希比对、算术运算产生的每个中间结果同样临时占格。上限定为一千而不是随全网存储水位上调,是刻意的取舍:验证程序的安全不该依赖”节点内存足够大”的假设,而要把单枚脚本的临时占用上界做成可以廉价算死的数字。配合元素尺寸与脚本长度上限,一台全节点为任意复杂脚本准备的临时内存都有清晰的最坏情况——这正是共识数字朴素却重要的原因。
风险提示:本文讨论脚本共识规则与节点政策层级,不构成投资建议;数值与执法位置以比特币核心 v31.0 源码为准,共识规则本身变更需要全网软分叉程序。
参考来源
- 比特币核心 v31.0 源码 src/script/script.h(GitHub):https://github.com/bitcoin/bitcoin/blob/v31.0/src/script/script.h
- 比特币核心 v31.0 源码 src/script/interpreter.cpp(GitHub):https://github.com/bitcoin/bitcoin/blob/v31.0/src/script/interpreter.cpp
- 比特币核心 v31.0 源码 src/policy/policy.h(GitHub):https://github.com/bitcoin/bitcoin/blob/v31.0/src/policy/policy.h
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。