> ## 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 저장소에 어떻게 나뉘어 담기고, 버전이 매겨진 머클 트리와 어큐뮬레이터로 어떻게 인증되며, 시간이 지나며 어떻게 프루닝되는지.

확정된 상태는 서로 반대 방향으로 잡아당기는 두 가지 요구를 만족해야 합니다. 실행은 어떤 키의 현재 값을 빠르게, 수백만 번 원합니다. 검증은 어떤 값이 특정 버전에서 네트워크가 말하는 바로 그 값이었다는 증명을 원합니다. 하나의 구조로 둘 다 처리하면 둘 다 나쁘게 처리하게 됩니다.

그래서 스토리지는 문제를 별도의 저장소로 쪼개고, 각 저장소를 자기 접근 패턴에 맞춰 만듭니다.

<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">상태 머클 저장소</span><span className="dg-s">버전별 희소 머클 트리, 대체된 노드를 추적하는 인덱스</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 저장소**는 키와 버전으로 주소가 매겨진 상태 값을 담습니다. 실행과 조회가 읽는 것이 이것입니다. 이 저장소는 **16개로 샤딩되어 있어서**, 한 블록의 쓰기가 하나의 인스턴스에서 경합하는 대신 독립된 16개의 RocksDB 인스턴스로 흩어집니다. 자주 접근되는 상태는 별도의 계층에 추가로 보관되므로, 활발한 마켓의 작업 집합을 체인이 지금까지 저장한 모든 것 사이에서 찾아낼 필요가 없습니다.

**상태 머클 저장소**는 인증 구조를 담습니다. 값을 증명할 수 있게 해 주는 트리 노드와, 어떤 노드가 대체되었는지 추적하는 인덱스입니다.

이렇게 나눠 두면 값만 필요한 읽기는 트리 순회 비용을 치르지 않고, 증명은 점 조회에 최적화된 저장소에서 다시 짜 맞출 필요가 없습니다.

<h2 id="authentication">
  인증
</h2>

두 개의 머클 구조가 서로 다른 일을 합니다.

<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">버전이 매겨진 희소 머클 트리</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>

**버전이 매겨진 희소 머클 트리**가 상태를 인증합니다. 모든 키는 자기 해시로 결정되는 위치를 가지며, 트리의 각 버전은 변하지 않은 노드를 공유합니다. 그래서 한 블록에서 키 하나를 쓰면 트리가 아니라 경로 하나가 추가됩니다. 특정 버전에서 어떤 키에 대한 증명은 그 키의 리프에서 네트워크가 확정한 루트까지의 경로입니다.

**어큐뮬레이터**는 순서를 인증합니다. 하나는 트랜잭션을, 다른 하나는 이벤트를 누적하며, 각각 지금까지 포함된 모든 것을 순서대로 담아 하나의 루트로 요약합니다. 체인을 보유하지 않고도 "이 트랜잭션은 체인의 이 위치에 있다"를 증명할 수 있는 것은 이 때문입니다.

둘을 합치면, 상태 트리는 *어떤 값이 무엇이었는지*를 증명하고 어큐뮬레이터는 *무슨 일이 어떤 순서로 일어났는지*를 증명합니다.

<h3 id="speculative-state">
  추측 실행 상태
</h3>

블록의 결과는 확정되기 전에 이미 존재합니다. 이를 영속 트리에 썼다가 블록이 확정되지 않으면 되돌리는 대신, 확정되지 않은 상태는 마지막으로 확정된 버전 위에 얹힌 **인메모리 희소 머클 오버레이**에 보관됩니다.

실행은 이 오버레이를 통해 읽으며 일관된 뷰를 봅니다. 블록이 확정되면 오버레이가 실체화됩니다. 확정되지 않으면 오버레이는 버려지고, 영속 저장소는 애초에 건드려진 적이 없습니다. 추측 실행이 스토리지에 잔해를 남기지 않는 이유가 이것입니다.

<h3 id="caching">
  캐싱
</h3>

트리 노드는 두 단계로 캐싱됩니다. 최근 버전을 계속 주소로 접근할 수 있게 유지하는 버전 인식 캐시, 그리고 그 아래의 LRU 캐시입니다. 트레이딩 체인의 접근 패턴, 즉 매 블록 건드려지는 소수의 뜨거운 키와 드물게 건드려지는 긴 꼬리가 바로 이런 캐시가 겨냥하는 형태입니다.

<h2 id="pruning">
  프루닝
</h2>

모든 버전을 영원히 보관하는 것은 선택이지 요구사항이 아닙니다. 세 저장소를 대상으로 각자의 보관 정책을 가진 독립된 프루너 세 개가 돌아갑니다. 원장에 하나, 상태 값에 하나, 머클 노드에 하나입니다.

머클 프루너와 상태 값 프루너는 데이터와 같은 시점에 기록되는 **stale 인덱스**로 구동됩니다. 어떤 버전이 노드나 값을 대체하면, 대체된 항목은 그 버전에서 stale로 기록됩니다. 그러면 프루닝은 쓰레기를 찾아다니는 일이 아니라 인덱스에 대한 범위 스캔이 됩니다. 무엇이 언제 수거 대상이 되는지는 쓰기 측이 이미 말해 두었기 때문입니다.

<Note>
  보관 기간은 실질적인 결과를 낳는 운영자의 결정입니다. 공격적으로 프루닝한 노드는 현재 상태를 효율적으로 제공하지만, 과거를 묻는 조회에 답할 수 없고 더 뒤에서 출발하는 노드에 상태 동기화를 제공할 수도 없습니다. 아카이브 노드는 전부 보관하고 그 비용을 치릅니다. [노드 운영](/ko/developers/run-a-node)을 참고하십시오.
</Note>

<h2 id="backup-and-restore">
  백업과 복원
</h2>

저장소는 실행 중인 노드와 무관하게 백업하고 복원할 수 있습니다. 덕분에 제네시스부터 재실행하는 대신 스냅숏으로 노드를 세울 수 있고, 백업을 믿는 대신 복원된 노드의 상태를 확정된 루트에 대조해 검증할 수 있습니다.

<h2 id="where-to-go-next">
  다음으로 읽을 문서
</h2>

<CardGroup cols={2}>
  <Card title="상태 동기화" href="/ko/protocol/architecture/state/sync">
    노드가 체인 전체를 재실행하지 않고 따라잡는 방법을 다룹니다.
  </Card>

  <Card title="상태 모델" href="/ko/protocol/architecture/state/model">
    무엇이 저장되고 있으며, 어느 표현이 권위를 갖는지 다룹니다.
  </Card>

  <Card title="인덱서" href="/ko/protocol/architecture/indexer">
    확정된 기록에서 이력을 재구성하는 일을 다룹니다.
  </Card>

  <Card title="노드 운영" href="/ko/developers/run-a-node">
    노드의 역할, 그리고 운영에 대해 문의하는 방법입니다.
  </Card>
</CardGroup>
