三个存储
已提交区块
账本存储交易 · 输出 · 事件 · 写集 · 累加器 · 区块元数据
重放与审计
状态 KV 存储当前值与历史值,分成十六个分片,高频状态另置一层
查询与执行
状态 Merkle 存储一棵带版本的稀疏 Merkle 树,外加跟踪被取代节点的索引
验证 — 证明
只需要取值的读取,不必为树遍历买单;而一份证明,也不必从一个为点查询优化的存储里硬凑出来。
认证
两种 Merkle 结构做的是不同的工作。带版本的稀疏 Merkle 树
累加器
你的键及其值
兄弟节点哈希
兄弟节点哈希
状态根,由共识提交
你的交易,或一个事件
交易累加器 · 事件累加器各自承诺到目前为止按顺序包含的全部内容
账本根,由共识提交
树证明某个值曾是什么;累加器证明发生了什么、以何种顺序发生。两者合起来,不必持有整条链,也能证明“这笔交易在链上的这个位置”。
推测状态
区块的结果在提交之前就已经存在。与其先写进持久化的树、区块万一没提交再撤回,不如把未提交状态放在最近一个已提交版本之上的一层内存稀疏 Merkle 覆盖层里。 执行透过这层覆盖读取,看到的是一致的视图。区块提交了,覆盖层就物化下去;没提交,覆盖层直接丢掉,持久化存储自始至终没被碰过。正是这一点让推测执行不会在存储里留下残渣。缓存
树节点在两个层次上缓存:一个感知版本的缓存,保持近期版本可寻址;其下是一个最近最少使用(LRU)缓存。一条交易链的访问模式——一小组每个区块都会触及的热点键,加上一条极少被触及的长尾——正是这类缓存所针对的形态。裁剪
每一个版本都永久留着,是选择,不是要求。三个相互独立的裁剪器分别针对这三个存储运行,各有自己的保留策略:一个针对账本,一个针对状态值,一个针对 Merkle 节点。 Merkle 裁剪器和状态值裁剪器由与数据同时写入的过期索引驱动。某个版本取代了一个节点或一个值,被取代的那一条就在这个版本上记为过期。于是裁剪变成对一个索引做区间扫描,而不是去搜寻垃圾——写入方早已说明了什么会变得可回收,以及在什么时候。保留策略是运维决定,后果实打实。裁剪激进的节点,当前状态照样提供得很快,历史查询却答不了,也没法给一个从更早处起步的节点做状态同步。归档节点保留一切,也为此付钱。参见运行节点。
备份与恢复
这些存储可以脱开运行中的节点单独备份、单独恢复。正因如此,才能从快照拉起一个节点,不必从创世重放;也才能对着已提交的根去验证恢复出来的状态,而不是选择相信那份备份。后续阅读
状态同步
节点如何在不重放全部历史的情况下追上这条链。
状态模型
存储的是什么,以及哪一种表示是权威。
索引器
从已提交记录中重建历史。
运行节点
节点角色,以及如何咨询节点运营事宜。