Skip to main content
在交易引擎里,有三样不同的东西都叫“状态”。把它们混为一谈,系统最后给出的答案就要看你问的是谁。本页把三者区分开,并说明哪一种是权威。

三种表示

引擎状态市场 · 账户 · 仓位 · 订单簿 · 清算所跨区块存续 · 一份重建
区块工作集标记价格 · 被触及账户 · 成交 · 暂存输出只存续一个区块 · 一本流水账
链上状态带版本的键值,由 Merkle 认证权威
它防住的那种失效内存视图已经和已提交内容漂开的引擎,照样一个接一个地给答案,而每一个都是错的——错在哪里,要等到结算才暴露。
引擎状态必须能从已提交的记录推导出来,反过来绝不可以
引擎状态是内核在区块之间拿着的东西:市场元数据、账户、仓位、订单簿、合约状态、清算所状态。它是工作表示——按撮合与清算实际的访问模式来布局,不按存储来布局。 区块工作集只在一个区块执行期间存在:区块开始时固定的标记价格、哪些账户和订单被动过、产生了哪些成交、正在组装的输出。它是这个区块里发生了什么的一份流水账。 链上状态是已提交的结果:带版本的键值条目,由 Merkle 结构认证,持久、可重放。节点同步的是它,证明针对的是它,索引器读的也是它。 链上状态是权威。 区块工作集是用来构建确定性输出的流水账,不是事实来源。引擎状态则是链上状态为执行而优化出来的一份重建:它必须能从已提交的记录里推导出来,反过来绝不可以。 把这个关系搞反,会引出一种特定而且认得出来的失效:引擎的内存视图已经和已提交内容漂开了,它却照样一个接一个地给答案,而每一个答案都是错的——错在哪里,要等到结算才暴露。

键

链上状态按键编址。交易状态归进具名、且显式带版本的命名空间——手续费配置、永续合约索引、杠杆档位表、管理角色列表,都是例子。 版本后缀不是装饰。一份配置的结构一变,它就迁到该键的新版本,旧键留着,好让迁移期间仍能读到按旧模式写入的状态。读取方如果把某个版本写死、而且从不重新检查,迁移之后就会悄无声息地读到过期配置;每次去解析当前键的读取方则不会。
这就是为什么配置应当从链上读,不要写死在客户端代码里。费率档位服务每个周期都去读链上实时的手续费配置,正是出于这个原因——档位表一旦焊死在客户端里,早晚会和网络实际套用的那一张对不上。

版本

每一个已提交的区块都会把版本往前推一格。状态值按写入时所处的版本存储,于是这个存储装的不只是“当前状态”,而是“任意版本上的状态”。 这一条性质一次带来好几件事:
  • 证明可以针对某个特定版本生成,不只是针对当下。
  • 重放可以从任意版本开始,不必从创世开始。
  • 读取可以是历史性的——索引器重建一个仓位的历史,是在要旧版本,不是在扫日志。
  • 裁剪变成一个策略问题:往回保留多久,而不再是结构上的硬限制。

最终被提交的是什么

内核每执行一个区块,产出两样东西。每笔交易带着自己的写入和事件,绑在这笔交易上。不属于任何单笔用户交易的影响——资金费率流转、保险基金变动、区块级计数器——走一条专门的系统通道。 两条合起来,什么都不会漏掉。内核内部不存在这样一种执行影响:既不在某笔交易的输出里,也不在系统通道里。正是这份完整性,让已提交的记录可以当作事情的全部,而不是它的一份摘要;也正是它,使得撮合哪怕成批运行,一个事件仍然能追回到导致它的那笔交易。

后续阅读

存储与证明

已提交状态如何在物理上存储、认证与裁剪。

状态同步

一个从未见过这条链的节点如何追上它。

IntentionKernel

引擎状态与区块工作集所在之处。

索引器

把已提交状态变成可查询的东西。