封闭世界执行
把指令集封闭起来,只是内核重复了四次的同一个动作的一个实例。每一次,可能发生的事情都被提前枚举完,落在集合之外的东西是被拒绝,而不是被处理。操作
不在枚举集合里的 payload——在验证阶段被拒,不是运行时
刚才被要求做的究竟是什么
真相
把每区块工作集当成权威。它是日志,引擎状态才是来源
此刻什么是真的
输入
任何在两台机器上会不同的东西:墙钟、熵、浮点、哈希序
换一个节点还是不是这些字节
因果
无主的状态变更。不属于任何用户交易的效果走系统通道,不是豁免
这一笔为什么会发生
封闭的是
拒绝的是
于是账本能回答
四重封闭,同一个动作:把空间提前枚举完,拒绝落在外面的一切。
指令集
参与者或验证者能做的每一件事,都属于一组固定的、带类型的操作:
载荷不属于这一集合的交易,在校验阶段就会被拒绝。这个集合只能通过协议升级改变,绝不会因为部署了什么新东西而改变。
执行一个区块
执行按固定的阶段序列走。这个次序不是实现细节——它决定一次强平能不能看到触发它的那个价格,也决定一笔撤单能不能抢在到来的吃单之前生效。价格固定本区块的标记价格
加载载入本区块的账户与订单
资金费结算资金费流
风险金库减仓 → 强平 → ADL
撮合撮合前 → 撮合 → 撮合后
收尾收集输出,清除无用状态
每个下游阶段面对的是每个品种一个价格,而不是一个一直在动的价格
按加载时的仓位结算,不按它们最终的样子
被迫产生的流量先了结,然后才放行新的自主流量
排在风险阶段之后,同一区块里的订单才抢不了强平的跑
归零的仓位和已清空的账户不会留存
一个区块,六个阶段
这个位置决定什么
条件单在这一序列里被扫描并推进,所以本区块标记价格触发的条件,就在本区块生效,不留到下一个区块。
强平跑在撮合前面,所以同一区块里的订单抢不了强平的跑。等自主订单开始撮合,强平产生的流量已经了结。
两类状态
内核严格区分什么会持久存在、什么只是临时草稿。区块执行期间
引擎状态市场 · 账户 · 订单簿 · 仓位 · 清算所跨区块存在
区块工作集标记价格 · 被触及账户 · 成交 · 暂存输出只在一个区块内存在
已提交链上状态带版本 · 可认证权威来源
脏数据跟踪很容易被误当成权威。区块内发生了哪些变化,是用来构造确定性输出的,状态本身并不住在那里。
引擎据以重建的权威来源
读取
应用
物化
结果为什么可复现
这里的确定性是被强制的,不是指望来的。每个诚实节点在同样的前置状态上执行同样的区块,都会得到逐字节相同的结果,因为执行路径上读不到任何在节点之间会有差异的输入:- 全程定点数运算。 结算数学跑在整数定点数上,舍入语义是显式的——保证金在负指数上向上取整,手续费按对哪一方有利分别向下或向上取整。结算路径上没有浮点:两台机器之间差一次舍入,就是一次分叉。
- 不用挂钟时间。 区块内的先后使用共识提交的规范位置和区块时间戳,绝不使用本地时间。
- 运行期没有随机性。 任何需要随机数的地方,都从链上状态确定性地导出。
- 确定性遍历。 任何遍历顺序会体现在输出中的集合,都是有序的,而不是哈希随机化的。
风险公式是纯的
保证金要求、强平价格、减仓对象选择、手续费、未平仓合约量上限——决定这些的公式,都实现为纯的、无状态的函数。它们接受值、返回值,既不读取也不修改账本状态。 状态变更完全由清算所驱动:清算所调用这些函数,并把结果落到状态上。持有状态的模块——账户、仓位、订单簿——存放数据并暴露修改方法,但自身不驱动业务流程。 这是一条刻意划出的边界。它意味着一次保证金计算可以脱离环境、对着一张输入输出表单独核对;也意味着任何人的余额要发生变化,有且只有一条代码路径。输出
执行产出状态写入和事件,每一项都绑定到引起它的那笔交易;此外还有一条通道,容纳不属于任何单笔用户交易的系统级影响——资金费、保险基金变动、区块计数器。这些构成交易输出,由状态层提交、由索引器对外提供。 内核里发生的事,下游没有看不见的。只要动了状态,它就出现在某个人的输出里,或者出现在系统通道里。后续阅读
撮合
订单簿、价格-时间优先,以及订单有效期和自成交防护如何在其上生效。
清算所
余额、仓位与保证金发生变化的唯一路径。
状态模型
内核的输出一旦被提交,会变成什么。
IntentionBFT
顺序和价格是从哪里来的。