Skip to main content
확정된 상태는 서로 반대 방향으로 잡아당기는 두 가지 요구를 만족해야 합니다. 실행은 어떤 키의 현재 값을 빠르게, 수백만 번 원합니다. 검증은 어떤 값이 특정 버전에서 네트워크가 말하는 바로 그 값이었다는 증명을 원합니다. 하나의 구조로 둘 다 처리하면 둘 다 나쁘게 처리하게 됩니다. 그래서 스토리지는 문제를 별도의 저장소로 쪼개고, 각 저장소를 자기 접근 패턴에 맞춰 만듭니다.

세 개의 저장소

확정된 블록
원장 저장소트랜잭션 · 출력 · 이벤트 · 쓰기 집합 · 어큐뮬레이터 · 블록 메타데이터
재실행과 감사
상태 KV 저장소현재 값과 과거 값, 열여섯 개 샤드, 자주 쓰는 상태는 별도 계층
조회와 실행
상태 머클 저장소버전별 희소 머클 트리, 대체된 노드를 추적하는 인덱스
검증 — 증명
값만 필요한 읽기는 트리 순회 비용을 치르지 않고, 증명은 점 조회에 최적화된 저장소에서 다시 짜 맞출 필요가 없습니다.
원장 저장소는 체인의 이력을 담습니다. 트랜잭션, 그 출력과 부가 데이터, 이벤트, 쓰기 집합, 블록 메타데이터, 그리고 어큐뮬레이터가 여기에 있습니다. 재실행의 대상이 되는 것이 이것입니다. 상태 KV 저장소는 키와 버전으로 주소가 매겨진 상태 값을 담습니다. 실행과 조회가 읽는 것이 이것입니다. 이 저장소는 16개로 샤딩되어 있어서, 한 블록의 쓰기가 하나의 인스턴스에서 경합하는 대신 독립된 16개의 RocksDB 인스턴스로 흩어집니다. 자주 접근되는 상태는 별도의 계층에 추가로 보관되므로, 활발한 마켓의 작업 집합을 체인이 지금까지 저장한 모든 것 사이에서 찾아낼 필요가 없습니다. 상태 머클 저장소는 인증 구조를 담습니다. 값을 증명할 수 있게 해 주는 트리 노드와, 어떤 노드가 대체되었는지 추적하는 인덱스입니다. 이렇게 나눠 두면 값만 필요한 읽기는 트리 순회 비용을 치르지 않고, 증명은 점 조회에 최적화된 저장소에서 다시 짜 맞출 필요가 없습니다.

인증

두 개의 머클 구조가 서로 다른 일을 합니다.
버전이 매겨진 희소 머클 트리
어큐뮬레이터
키와 그 값
형제 해시
형제 해시
상태 루트, 합의가 확정
트랜잭션 또는 이벤트
트랜잭션 어큐뮬레이터 · 이벤트 어큐뮬레이터각각 지금까지 포함된 모든 것을 순서대로 확정
원장 루트, 합의가 확정
트리는 어떤 값이 무엇이었는지를 증명하고, 어큐뮬레이터는 무슨 일이 어떤 순서로 일어났는지를 증명합니다. 둘을 합치면 체인을 보유하지 않고도 “이 트랜잭션은 체인의 이 위치에 있다”를 증명할 수 있습니다.
버전이 매겨진 희소 머클 트리가 상태를 인증합니다. 모든 키는 자기 해시로 결정되는 위치를 가지며, 트리의 각 버전은 변하지 않은 노드를 공유합니다. 그래서 한 블록에서 키 하나를 쓰면 트리가 아니라 경로 하나가 추가됩니다. 특정 버전에서 어떤 키에 대한 증명은 그 키의 리프에서 네트워크가 확정한 루트까지의 경로입니다. 어큐뮬레이터는 순서를 인증합니다. 하나는 트랜잭션을, 다른 하나는 이벤트를 누적하며, 각각 지금까지 포함된 모든 것을 순서대로 담아 하나의 루트로 요약합니다. 체인을 보유하지 않고도 “이 트랜잭션은 체인의 이 위치에 있다”를 증명할 수 있는 것은 이 때문입니다. 둘을 합치면, 상태 트리는 어떤 값이 무엇이었는지를 증명하고 어큐뮬레이터는 무슨 일이 어떤 순서로 일어났는지를 증명합니다.

추측 실행 상태

블록의 결과는 확정되기 전에 이미 존재합니다. 이를 영속 트리에 썼다가 블록이 확정되지 않으면 되돌리는 대신, 확정되지 않은 상태는 마지막으로 확정된 버전 위에 얹힌 인메모리 희소 머클 오버레이에 보관됩니다. 실행은 이 오버레이를 통해 읽으며 일관된 뷰를 봅니다. 블록이 확정되면 오버레이가 실체화됩니다. 확정되지 않으면 오버레이는 버려지고, 영속 저장소는 애초에 건드려진 적이 없습니다. 추측 실행이 스토리지에 잔해를 남기지 않는 이유가 이것입니다.

캐싱

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

프루닝

모든 버전을 영원히 보관하는 것은 선택이지 요구사항이 아닙니다. 세 저장소를 대상으로 각자의 보관 정책을 가진 독립된 프루너 세 개가 돌아갑니다. 원장에 하나, 상태 값에 하나, 머클 노드에 하나입니다. 머클 프루너와 상태 값 프루너는 데이터와 같은 시점에 기록되는 stale 인덱스로 구동됩니다. 어떤 버전이 노드나 값을 대체하면, 대체된 항목은 그 버전에서 stale로 기록됩니다. 그러면 프루닝은 쓰레기를 찾아다니는 일이 아니라 인덱스에 대한 범위 스캔이 됩니다. 무엇이 언제 수거 대상이 되는지는 쓰기 측이 이미 말해 두었기 때문입니다.
보관 기간은 실질적인 결과를 낳는 운영자의 결정입니다. 공격적으로 프루닝한 노드는 현재 상태를 효율적으로 제공하지만, 과거를 묻는 조회에 답할 수 없고 더 뒤에서 출발하는 노드에 상태 동기화를 제공할 수도 없습니다. 아카이브 노드는 전부 보관하고 그 비용을 치릅니다. 노드 운영을 참고하십시오.

백업과 복원

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

다음으로 읽을 문서

상태 동기화

노드가 체인 전체를 재실행하지 않고 따라잡는 방법을 다룹니다.

상태 모델

무엇이 저장되고 있으며, 어느 표현이 권위를 갖는지 다룹니다.

인덱서

확정된 기록에서 이력을 재구성하는 일을 다룹니다.

노드 운영

노드의 역할, 그리고 운영에 대해 문의하는 방법입니다.