메인 콘텐츠로 건너뛰기

목적 및 범위

이 문서는 StableNet에서 모든 계정 잔액, nonce, 컨트랙트 코드, 컨트랙트 저장소를 포함하는 전역 상태를 유지·관리하는 상태 관리 시스템을 설명합니다.
상태 관리 레이어는 Merkle Patricia Trie 구조 위에 캐싱 인터페이스를 제공하며, 트랜잭션 실행 중 발생하는 상태 전환을 추적하고, 블록 단위로 상태를 커밋하는 역할을 수행합니다.
관련 문서:

StateDB 아키텍처

StateDB는 상태 접근과 수정을 담당하는 핵심 인터페이스입니다. core/state/statedb.go에 정의되어 있으며, 계정 trie 위에 메모리 캐싱 레이어를 제공하고 개별 계정을 나타내는 상태 객체의 전체 라이프사이클을 관리합니다. 핵심 구성 요소:
  • trie: 계정 상태를 저장하는 계정 trie
  • snaps: 빠른 상태 조회를 위한 스냅샷 트리 (선택적)
  • stateObjects: 메모리 내 계정 객체 캐시 (map[common.Address]*stateObject)
  • journal: 상태 변경을 기록하는 저널, 롤백에 사용
  • originalRoot: 현재 블록 처리 시작 시점의 상태 루트
StateDB는 블록 실행 시작 시 특정 상태 루트를 기준으로 생성되며, 블록 내 트랜잭션 실행 동안 상태 변경을 누적한 뒤, 블록 종료 시 새로운 상태 루트를 계산하고 커밋합니다.

상태 객체

stateObject는 단일 계정의 상태를 표현하는 내부 구조체로, 잔액, nonce, 코드, 저장소를 포함합니다. 메모리에 캐시되며, 변경 중인 상태와 커밋된 상태를 명확히 구분하여 관리합니다.

StateAccount 구조

각 계정의 영구 데이터는 core/types/state_account.go에 정의된 StateAccount 구조체에 저장됩니다:
필드타입설명
Nonceuint64트랜잭션 카운터
Balance*uint256.IntWei 단위 계정 잔액
Rootcommon.Hash저장소 trie 루트
CodeHash[]byte컨트랙트 코드 해시
Extrauint64StableNet 확장 필드 (계정 정책 플래그)
Extra 필드는 StableNet 확장으로, 거버넌스 레이어에서 정의한 계정 정책 상태를 표현하는 데 사용됩니다.

상태 객체 라이프사이클

상태 객체는 다음 단계를 거쳐 관리됩니다:
  1. 생성/로드: getStateObject()가 캐시에서 조회하거나, 없을 경우 trie 또는 스냅샷에서 로드
  2. 수정: 트랜잭션 실행 중 잔액, nonce, 저장소 값 변경이 메모리에 기록
  3. 최종화: Finalise() 호출 시 dirty 상태가 pending 상태로 전환
  4. 커밋: Commit() 호출 시 변경 사항이 trie에 반영되고 데이터베이스에 저장

Trie 구조

상태는 계층적인 Merkle Patricia Trie 구조로 관리됩니다:
  • 계정 trie (Account Trie): 모든 계정의 상태를 저장하는 단일 trie, 키는 주소 해시
  • 저장소 trie (Storage Trie): 각 컨트랙트 계정마다 존재하며, 컨트랙트 변수 값을 저장
주요 연산은 다음과 같습니다:
작업StateDB 메서드설명
계정 조회getStateObject()계정 상태 로드
계정 기록updateStateObject()계정 상태 갱신
계정 삭제deleteStateObject()계정 제거
저장소 조회GetState()저장소 슬롯 조회
저장소 기록SetState()저장소 슬롯 갱신

캐싱 레이어

상태 접근 비용을 줄이기 위해 다단계 캐싱 구조를 사용합니다:
요청 → StateDB 캐시 → 스냅샷 레이어 → Trie 노드 캐시 → 디스크 데이터베이스
  1. StateDB 캐시
    stateObject 인스턴스를 메모리에 유지합니다. 현재 트랜잭션에서 변경된 dirtyStorage, 최종화되었으나 커밋되지 않은 pendingStorage, 변경 추적을 위한 originStorage를 관리합니다.
  2. 스냅샷 레이어
    계정과 저장소를 평면 키-값 구조로 제공하여 trie 순회를 우회합니다. 일반 상태 조회 성능을 크게 개선하며, 비동기적으로 생성·갱신됩니다.
  3. Trie 노드 캐시
    trie 데이터베이스가 관리하며, 최근 접근 노드(clean)와 아직 디스크에 기록되지 않은 수정 노드(dirty)를 분리해 보관합니다.
  4. 디스크 데이터베이스
    최종 영구 저장소로, 인코딩된 trie 노드와 상태 데이터를 저장합니다.

저널링 및 스냅샷

저널 시스템은 모든 상태 변경을 순서대로 기록하여, 트랜잭션 단위의 롤백을 가능하게 합니다. 실행 중 오류가 발생하면 해당 트랜잭션에서 발생한 모든 상태 변경을 정확히 되돌릴 수 있습니다.

스냅샷 및 되돌리기

  • Snapshot(): 현재 상태의 되돌리기 기준점을 생성하며, 저널 인덱스를 반환합니다
  • RevertToSnapshot(): 지정된 스냅샷 이후의 모든 변경을 역순으로 복구합니다
저널에는 다음과 같은 변경 유형이 기록됩니다:
  • 잔액, nonce, 코드 변경
  • 저장소 슬롯 변경
  • selfdestruct 플래그
  • 접근 목록(access list) 변경
  • 로그 추가
각 EVM 호출 전 스냅샷이 생성되며, 실행이 실패하면 해당 호출 범위의 상태 변경만 롤백됩니다.

상태 커밋

상태 커밋은 블록 단위로 수행되며, 변경 사항을 영구 저장소에 반영하는 과정입니다.

커밋 흐름

Finalise → IntermediateRoot → Commit
1. Finalise
  • dirty 저장소를 pending 저장소로 전환
  • 빈 계정 정리(EIP-161)
  • 트랜잭션 실행 후 반복 호출
2. IntermediateRoot
  • 각 저장소 trie의 루트 계산
  • 계정 trie 갱신
  • 현재 상태 루트 반환, 영수증 루트 계산에 사용
3. Commit
  • 계정 trie와 저장소 trie를 디스크에 커밋
  • trie 데이터베이스 갱신
  • 스냅샷 커밋
  • 최종 상태 루트 반환

상태 변경 추적

경로 기반 상태 스키마(PBSS)에서는 효율적인 상태 관리와 프루닝을 위해 다음 추적 맵을 유지합니다:
  • accounts / storages: 현재 블록에서 변경된 계정 및 저장소
  • accountsOrigin / storagesOrigin: 변경 전 원본 값
  • stateObjectsDestruct: 삭제된 계정 정보
이 정보는 커밋 시 trie 데이터베이스로 전달되어 상태 관리 최적화에 사용됩니다.

데이터베이스 통합

StateDB는 core/state/database.goDatabase 인터페이스를 통해 trie 데이터베이스와 통합됩니다. 해시 기반 스키마와 경로 기반(PBSS) 스키마를 모두 지원합니다.

데이터베이스 구성

설정기본값설명
StateSchemeHashScheme상태 저장 스키마
TrieCleanLimit256 MBClean 캐시 크기
TrieDirtyLimit256 MBDirty 캐시 크기
SnapshotLimit256 MB스냅샷 캐시 크기
PreimagesfalseTrie 키 프리이미지 저장 여부
StateHistory0상태 히스토리 보관 블록 수
주요 인터페이스 메서드:
  • OpenTrie(root)
  • OpenStorageTrie(stateRoot, addrHash, root, trie)
  • ContractCode(addrHash, codeHash)
  • TrieDB()

상태 프리페칭

상태 프리페칭은 블록 실행 중 필요한 trie 노드를 사전에 로드하는 최적화 기법입니다.
StartPrefetcher()로 시작되고 StopPrefetcher()로 종료됩니다.
프리페처는 트랜잭션이 접근할 계정과 저장소 슬롯을 예측하여 캐시에 적재함으로써, 디스크 I/O 대기를 줄이고 블록 처리 성능을 향상시킵니다.

성능 메트릭

StateDB는 상태 관리 성능을 모니터링하기 위해 다음 메트릭을 수집합니다:
  • 계정 및 저장소 읽기/쓰기/커밋 시간
  • 스냅샷 조회 및 커밋 시간
  • Trie 데이터베이스 커밋 시간
  • 변경된 계정 및 저장소 슬롯 수
이 메트릭은 노드 운영자가 상태 처리 병목을 분석하고 성능을 튜닝하는 데 활용됩니다.