Starknet 的裁判是一段程序:SNOS 怎么把一个区块变成可证明的陈述 图 1
Starknet 的裁判是一段程序:SNOS 怎么把一个区块变成可证明的陈述 · 图 1

Starknet 把”一个区块是合法的”这句模糊的话,压缩成了一句机器可证的话:某段程序以某些输入运行,得到某个输出。承担压缩工作的就是 SNOS——Starknet Operating System。它不是常驻节点的内核进程,而是一段 Cairo 程序,证明者必须借它把整块交易走一遍,才拿得出以太坊认的证明。本文按官方文档拆解这个”操作系统”实际在做什么。

只有程序输入输出才能被证明

Cairo 的证明系统只认一类陈述:“某个具体程序用具体输入跑出具体输出”。于是”这个 Starknet 区块合法”必须被翻译成程序语言:SNOS 以初始状态和一批交易为输入,顺序执行后输出这些交易作用后的新状态。文档由此推出一个重要身份——SNOS 是”交易是否正确执行”的最终裁判。它举的例子很具体:作恶的排序器可以跳过账户合约的校验函数、直接调执行函数来歪曲 INVOKE 交易的语义,但调用顺序的强制写在 SNOS 代码里,跳步的排序器根本产不出这个区块的证明,篡改就堵在证环节。

程序内部四步

官方文档把 SNOS 的执行草描为四段。第一段预处理,把后面要用到的见证数据当提示备齐:每个被访问存储槽的默克尔路径、每个被访问合约的代码。第二段逐笔执行交易:把发起账户合约装进内存并调用,内部再调用的合约按”地址与类哈希”记入一张全局状态更新字典,为的是后续核对装进内存的代码与状态承诺里的类哈希对得上。这里有个反直觉设计——合约调用是非确定性的:程序先猜调用结果继续跑,猜错在证明阶段兜不住。第三段核对新状态承诺,要求它等于把状态更新字典施加到旧承诺的结果。第四段把状态差异编码进输出段,供数据可用性发布。

图:一段程序读入初始状态与交易流水、输出新状态承诺的证成示意

名字里没有的权限边界

SNOS 全程没有”决定权”:它不选交易、不管排序,甚至大多数时候不主动取数据——系统调用把控制权从合约交回 SNOS 本体,读取存储、跨合约调用都靠它代答加验证。它的权力只有一样:凡是不符合它对语义理解的执行轨迹,出不了证明。

文档还刻意区分了同一个区块的”两种执行”。第一次由排序器完成,怎么跑随它选——甚至可以不走 Cairo 虚拟机,直接把合约预编译成机器码原生执行;第二次由证明者完成,此时区块早已定稿,访问了哪些槽、每个内部调用返回什么全都已知,猜测成本近乎为零。排序器还可以施加一些 SNOS 不管的私有限制,比如为防拒绝服务而给校验函数设的上限,只要这些限制只保护排序器自己、不碰执行正确性,协议就允许。但两次执行必须在语义上完全一致:一旦 SNOS 不认,排序器就交不出该区块的证明,唯一的出路是在 Starknet 上重组。

证明还要再包一层

从 v0.13.2 起,光有 SNOS 运行的证明已经不够更新以太坊上的状态:需要提交一个叫应用引导程序的证明,它核验一段或多段基准程序(也就是 SNOS)运行证明,把输出交给聚合程序压扁多个区块的状态差异,一条证明覆盖一段区块区间。为此 Starknet 核心合约里登记着 SNOS 与聚合程序的程序哈希,破坏性协议变更必须同步更新这份哈希——文档甚至直接给了以太坊浏览器上两个只读函数的入口,普通用户可以自查。核心合约还替 SNOS 补上它自己看不见的两件事:输入状态是否就是以太坊上记录的当前状态、L1 到 L2 的消息是否真的在以太坊发出过。

SNOS 本体代码开源在 cairo-lang 仓库,但跑起来还需要提示实现的配合:Python 提示实现已被标注弃用、不再随后续版本维护,替代者是经全节点连接自动备齐输入的 Rust 实现。读代码核验时,这两套实现的边界要分清楚。

对信任假设的影响

把 SNOS 放进整条链路看:排序器执行、证明者拿 SNOS 程序产证明、以太坊上的验证合约校验证明——状态在 L1 前进的前提,是存在一份对”SNOS 从旧状态到新状态”这条计算的合法证明。排序器因此拥有的是排序与包含权,不是任意改写执行语义的权力;同时,SNOS 程序本身的正确性、以及证明系统对它的忠实度,成为整条安全链的地基假设之一。评估一条 validity rollup 时,值得问的三个问题在这里都有具体落点:状态转换逻辑定义在哪段代码里、谁在实现它、实现有几种。这类判断与短期性能数字无关,也不构成对任何链的投资建议。