> ## Documentation Index
> Fetch the complete documentation index at: https://docs.intention.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# 存储与证明

> 已提交状态如何持久化到三个 RocksDB 存储中，如何由带版本的 Merkle 树和累加器提供认证，以及如何随时间裁剪。

已提交状态必须满足两项相互拉扯的需求。执行需要快速拿到某个键的当前值，每秒数百万次。验证需要一份证明，说明在某个特定版本上该值确实就是网络所声称的那个。用一种结构同时服务两者，只会两件事都做不好。

所以存储把问题拆到几个彼此独立的存储里，每一个都按自己的访问模式来设计。

<h2 id="three-stores">
  三个存储
</h2>

<div className="dg" data-dg="storage-stores">
  <div className="dg-c" style={{aspectRatio:"720 / 332"}}>
    <svg className="dg-w" viewBox="0 0 720 332" aria-hidden="true">
      <path className="dg-wire dg--green dg-dash" d="M 434.00 48.00 L 459.60 48.00" />

      <path className="dg-head dg--green" d="M 466.00 48.00 L 459.60 52.40 L 459.60 43.60 Z" />

      <path className="dg-wire dg--green dg-dash" d="M 434.00 144.00 L 459.60 144.00" />

      <path className="dg-head dg--green" d="M 466.00 144.00 L 459.60 148.40 L 459.60 139.60 Z" />

      <path className="dg-wire dg--green dg-dash" d="M 434.00 240.00 L 459.60 240.00" />

      <path className="dg-head dg--green" d="M 466.00 240.00 L 459.60 244.40 L 459.60 235.60 Z" />

      <path className="dg-wire dg--blue dg-soft" d="M 135.00 144.00 L 172.54 53.91" />

      <path className="dg-head dg--blue" d="M 175.00 48.00 L 176.60 55.60 L 168.48 52.22 Z" />

      <path className="dg-wire dg--blue dg-soft" d="M 135.00 144.00 L 168.60 144.00" />

      <path className="dg-head dg--blue" d="M 175.00 144.00 L 168.60 148.40 L 168.60 139.60 Z" />

      <path className="dg-wire dg--blue dg-soft" d="M 135.00 144.00 L 172.54 234.09" />

      <path className="dg-head dg--blue" d="M 175.00 240.00 L 168.48 235.78 L 176.60 232.40 Z" />
    </svg>

    <div className="dg-b dg--blue" style={{left:"0.0000%",top:"31.3253%",width:"18.0556%",height:"24.0964%"}}><span className="dg-t">已提交区块</span></div>
    <div className="dg-b dg--sky dg-left" style={{left:"25.0000%",top:"2.4096%",width:"34.7222%",height:"24.0964%"}}><span className="dg-t">账本存储</span><span className="dg-s">交易 · 输出 · 事件 · 写集 · 累加器 · 区块元数据</span></div>
    <div className="dg-b dg--green" style={{left:"65.2778%",top:"2.4096%",width:"34.7222%",height:"24.0964%"}}><span className="dg-t">重放与审计</span></div>
    <div className="dg-b dg--sky dg-left" style={{left:"25.0000%",top:"31.3253%",width:"34.7222%",height:"24.0964%"}}><span className="dg-t">状态 KV 存储</span><span className="dg-s">当前值与历史值，分成十六个分片，高频状态另置一层</span></div>
    <div className="dg-b dg--green" style={{left:"65.2778%",top:"31.3253%",width:"34.7222%",height:"24.0964%"}}><span className="dg-t">查询与执行</span></div>
    <div className="dg-b dg--sky dg-left" style={{left:"25.0000%",top:"60.2410%",width:"34.7222%",height:"24.0964%"}}><span className="dg-t">状态 Merkle 存储</span><span className="dg-s">一棵带版本的稀疏 Merkle 树，外加跟踪被取代节点的索引</span></div>
    <div className="dg-b dg--green" style={{left:"65.2778%",top:"60.2410%",width:"34.7222%",height:"24.0964%"}}><span className="dg-t">验证 — 证明</span></div>
    <div className="dg-free" style={{left:"0.0000%",top:"89.1566%",width:"100.0000%"}}><div className="dg-n">只需要取值的读取，不必为树遍历买单；而一份证明，也不必从一个为点查询优化的存储里硬凑出来。</div></div>
  </div>
</div>

**账本存储**保存链的历史：交易，交易的输出与辅助数据，事件，写集，区块元数据，以及累加器。这是你用来重放的东西。

**状态 KV 存储**保存状态值，按键和版本编址。这是执行与查询读取的东西。它被**分成十六个分片**，因此一个区块的写入会散布到十六个相互独立的 RocksDB 实例上，而不是争抢同一个。高频访问的状态另外放在自己的一层里，活跃市场的工作集就不必到链上有史以来存过的所有东西里去翻。

**状态 Merkle 存储**保存认证结构——用来证明某个值的树节点，以及跟踪哪些节点已被取代的索引。

拆开的好处是：只需要取值的读取，不必为树遍历买单；而一份证明，也不必从一个为点查询优化的存储里硬凑出来。

<h2 id="authentication">
  认证
</h2>

两种 Merkle 结构做的是不同的工作。

<div className="dg" data-dg="storage-proofs">
  <div className="dg-c" style={{aspectRatio:"720 / 400"}}>
    <svg className="dg-w" viewBox="0 0 720 400" aria-hidden="true">
      <path className="dg-wire" d="M 173.50 98.50 L 173.50 110.10" />

      <path className="dg-head" d="M 173.50 116.50 L 169.10 110.10 L 177.90 110.10 Z" />

      <path className="dg-wire" d="M 173.50 169.00 L 173.50 180.60" />

      <path className="dg-head" d="M 173.50 187.00 L 169.10 180.60 L 177.90 180.60 Z" />

      <path className="dg-wire" d="M 173.50 239.50 L 173.50 251.10" />

      <path className="dg-head" d="M 173.50 257.50 L 169.10 251.10 L 177.90 251.10 Z" />

      <path className="dg-wire" d="M 546.50 122.00 L 546.50 133.60" />

      <path className="dg-head" d="M 546.50 140.00 L 542.10 133.60 L 550.90 133.60 Z" />

      <path className="dg-wire" d="M 546.50 216.00 L 546.50 227.60" />

      <path className="dg-head" d="M 546.50 234.00 L 542.10 227.60 L 550.90 227.60 Z" />
    </svg>

    <div className="dg-band" style={{left:"0.0000%",top:"6.5000%",width:"48.1944%",height:"72.5000%"}}><span className="dg-cap">带版本的稀疏 Merkle 树</span></div>
    <div className="dg-band" style={{left:"51.8056%",top:"6.5000%",width:"48.1944%",height:"72.5000%"}}><span className="dg-cap">累加器</span></div>
    <div className="dg-b dg--blue" style={{left:"2.2222%",top:"12.5000%",width:"43.7500%",height:"11.1250%"}}><span className="dg-t">你的键及其值</span></div>
    <div className="dg-b" style={{left:"2.2222%",top:"30.1250%",width:"43.7500%",height:"11.1250%"}}><span className="dg-t">兄弟节点哈希</span></div>
    <div className="dg-b" style={{left:"2.2222%",top:"47.7500%",width:"43.7500%",height:"11.1250%"}}><span className="dg-t">兄弟节点哈希</span></div>
    <div className="dg-b dg--green" style={{left:"2.2222%",top:"65.3750%",width:"43.7500%",height:"11.1250%"}}><span className="dg-t">状态根，由共识提交</span></div>
    <div className="dg-b dg--blue" style={{left:"54.0278%",top:"12.5000%",width:"43.7500%",height:"17.0000%"}}><span className="dg-t">你的交易，或一个事件</span></div>
    <div className="dg-b" style={{left:"54.0278%",top:"36.0000%",width:"43.7500%",height:"17.0000%"}}><span className="dg-t">交易累加器 · 事件累加器</span><span className="dg-s">各自承诺到目前为止按顺序包含的全部内容</span></div>
    <div className="dg-b dg--green" style={{left:"54.0278%",top:"59.5000%",width:"43.7500%",height:"17.0000%"}}><span className="dg-t">账本根，由共识提交</span></div>
    <div className="dg-b dg-dashed dg-left" style={{left:"0.0000%",top:"83.0000%",width:"100.0000%",height:"14.5000%"}}><span className="dg-s">树证明某个值曾是什么；累加器证明发生了什么、以何种顺序发生。两者合起来，不必持有整条链，也能证明“这笔交易在链上的这个位置”。</span></div>
  </div>
</div>

**一棵带版本的稀疏 Merkle 树**为状态提供认证。每个键的位置由其哈希决定，而树的每个版本都共享那些没有变化的节点——所以在一个区块里写入一个键，增加的是一条路径，而不是一棵树。某个键在某个版本上的证明，就是一条路径：从这个键的叶子，走到网络提交的那个根。

**累加器**为顺序提供认证。一个累加交易，一个累加事件，各自产生一个根，承诺了到目前为止按顺序包含的全部内容。正因如此，不必持有整条链，也能证明“这笔交易在链上的这个位置”。

两者之间的分工：状态树证明*某个值曾是什么*，累加器证明*发生了什么以及以何种顺序发生*。

<h3 id="speculative-state">
  推测状态
</h3>

区块的结果在提交之前就已经存在。与其先写进持久化的树、区块万一没提交再撤回，不如把未提交状态放在最近一个已提交版本之上的一层**内存稀疏 Merkle 覆盖层**里。

执行透过这层覆盖读取，看到的是一致的视图。区块提交了，覆盖层就物化下去；没提交，覆盖层直接丢掉，持久化存储自始至终没被碰过。正是这一点让推测执行不会在存储里留下残渣。

<h3 id="caching">
  缓存
</h3>

树节点在两个层次上缓存：一个感知版本的缓存，保持近期版本可寻址；其下是一个最近最少使用（LRU）缓存。一条交易链的访问模式——一小组每个区块都会触及的热点键，加上一条极少被触及的长尾——正是这类缓存所针对的形态。

<h2 id="pruning">
  裁剪
</h2>

每一个版本都永久留着，是选择，不是要求。三个相互独立的裁剪器分别针对这三个存储运行，各有自己的保留策略：一个针对账本，一个针对状态值，一个针对 Merkle 节点。

Merkle 裁剪器和状态值裁剪器由与数据同时写入的**过期索引**驱动。某个版本取代了一个节点或一个值，被取代的那一条就在这个版本上记为过期。于是裁剪变成对一个索引做区间扫描，而不是去搜寻垃圾——写入方早已说明了什么会变得可回收，以及在什么时候。

<Note>
  保留策略是运维决定，后果实打实。裁剪激进的节点，当前状态照样提供得很快，历史查询却答不了，也没法给一个从更早处起步的节点做状态同步。归档节点保留一切，也为此付钱。参见[运行节点](/zh/developers/run-a-node)。
</Note>

<h2 id="backup-and-restore">
  备份与恢复
</h2>

这些存储可以脱开运行中的节点单独备份、单独恢复。正因如此，才能从快照拉起一个节点，不必从创世重放；也才能对着已提交的根去验证恢复出来的状态，而不是选择相信那份备份。

<h2 id="where-to-go-next">
  后续阅读
</h2>

<CardGroup cols={2}>
  <Card title="状态同步" href="/zh/protocol/architecture/state/sync">
    节点如何在不重放全部历史的情况下追上这条链。
  </Card>

  <Card title="状态模型" href="/zh/protocol/architecture/state/model">
    存储的是什么，以及哪一种表示是权威。
  </Card>

  <Card title="索引器" href="/zh/protocol/architecture/indexer">
    从已提交记录中重建历史。
  </Card>

  <Card title="运行节点" href="/zh/developers/run-a-node">
    节点角色，以及如何咨询节点运营事宜。
  </Card>
</CardGroup>
