链上时间从哪来?Sui 的 Clock 对象与共识时间边界 图 1
链上时间从哪来?Sui 的 Clock 对象与共识时间边界 · 图 1

在以太坊上写合约,任何交易看到的块时间戳都是同一个数;在 Sui 上情况不同:合约确实能读到一个 Unix 毫秒时间戳,但这个时间从哪里来、谁能读、读了要不要过共识,规则完全换了一套。Sui 官方文档把这套机制放在一个叫 Clock 的系统对象上。本文按 docs.sui.io 拆解它的位置、更新节奏与使用边界,帮助开发者与链上数据读者判断”链上时间”这四个字在 Sui 上到底意味着什么。

创世时就存在的唯一实例

Clock 在 Sui 里是一个共享对象,创世时在地址 0x6 创建,全局只有这一个实例,用户和包都不能再造新的。Move 入口函数要读时间,必须把这个 Clock 的不可变引用作为参数传进去;官方文档写得很硬:试图以可变引用或值的形式接收 Clock 的入口函数无法通过验证,诚实验证者也不会给这类交易签名或执行。这条规则不是风格偏好,而是并发模型的一部分——只读引用意味着两个都只用时间的交易之间没有写冲突,不必为了时间戳互相排队。

唯一系统对象为共识提交刷新时间、交易沿光带读取它的抽象示意

时间随共识提交向前跳

Clock 内部的时间戳不是节点各自看表得出的。系统在每个共识提交序列开头跑一笔系统交易(consensus commit prologue),把共识带回来的时间写进 Clock。文档给出的节奏参考是:共识提交大约每四分之一秒发生一次,也就是说链上可读的时间以这个粒度向前推进。同一笔交易里多次调用时间读取函数永远返回同一个值;跨交易看,触碰同一共享对象的成功交易看到的时间戳单调不减。它是一份”链同意的时间”,而不是某台机器上的墙钟。

过共识的快路径分界

Sui 把交易分成两条路:只碰自己独有对象的交易可以走免共识快路径,碰共享对象的交易必须进 Mysticeti 共识排队。而 Clock 本身就是共享对象——文档因此明确:任何需要读 Clock 的交易都必须走共识,快路径交易用不了这个函数。这对设计者是个实打实的取舍:给合约加一个”当前时间”参数,等于把这笔交易从最便宜的那条路挪到共识那条路上。想要时间的另一条缝是交易上下文里的纪元起始时间戳:它对全部交易可用(包括不走共识的),但大约每 24 小时才随纪元更替换一次值,只够做以天为粒度的逻辑。

对索引器与对账工具还有一层细节:每笔检查点开头的系统交易本身就携带共识时间戳字段,逐块核对时可以直接读它,不必模拟 Clock 状态;而检查点内的交易顺序来自共识输出,共享对象的相对先后以此为准。换句话说,链上时间有两条互为印证的读法——合约里读 0x6,链外读共识提交前言里的时间字段——两者应当逐提交一致,不一致本身就是异常信号。

读时间之前先问用途

把这两档放在一起,Sui 的意图很清楚:需要秒级新鲜度的逻辑(例如带截止期的拍卖、与外部时钟联动的结算)用 Clock,并接受共识成本;只需要”今天是哪个纪元”这类粗粒度状态用纪元时间戳,保住快路径。链上数据核验侧同理——看到一笔交易引用了 0x6,就能推断它经过了共识排序;反之,宣称”按链上时间执行”却完全没碰 Clock 的合约,用的多半是纪元边界这种更粗的口径。判断依赖强度之前,先看它读的是哪一档时间。

本文只作机制解释,时间粒度与共识节奏等参数可能随协议版本演进,请以官方文档为准;不构成任何投资建议。