목적 및 범위
이 문서는 StableNet에서 모든 계정 잔액, nonce, 컨트랙트 코드, 컨트랙트 저장소를 포함하는 전역 상태를 유지·관리하는 상태 관리 시스템을 설명합니다.상태 관리 레이어는 Merkle Patricia Trie 구조 위에 캐싱 인터페이스를 제공하며, 트랜잭션 실행 중 발생하는 상태 전환을 추적하고, 블록 단위로 상태를 커밋하는 역할을 수행합니다. 관련 문서:
StateDB 아키텍처
StateDB는 상태 접근과 수정을 담당하는 핵심 인터페이스입니다. core/state/statedb.go에 정의되어 있으며, 계정 trie 위에 메모리 캐싱 레이어를 제공하고 개별 계정을 나타내는 상태 객체의 전체 라이프사이클을 관리합니다.
핵심 구성 요소:
- trie: 계정 상태를 저장하는 계정 trie
- snaps: 빠른 상태 조회를 위한 스냅샷 트리 (선택적)
- stateObjects: 메모리 내 계정 객체 캐시 (
map[common.Address]*stateObject) - journal: 상태 변경을 기록하는 저널, 롤백에 사용
- originalRoot: 현재 블록 처리 시작 시점의 상태 루트
상태 객체
stateObject는 단일 계정의 상태를 표현하는 내부 구조체로, 잔액, nonce, 코드, 저장소를 포함합니다. 메모리에 캐시되며, 변경 중인 상태와 커밋된 상태를 명확히 구분하여 관리합니다.
StateAccount 구조
각 계정의 영구 데이터는core/types/state_account.go에 정의된 StateAccount 구조체에 저장됩니다:
| 필드 | 타입 | 설명 |
|---|---|---|
Nonce | uint64 | 트랜잭션 카운터 |
Balance | *uint256.Int | Wei 단위 계정 잔액 |
Root | common.Hash | 저장소 trie 루트 |
CodeHash | []byte | 컨트랙트 코드 해시 |
Extra | uint64 | StableNet 확장 필드 (계정 정책 플래그) |
Extra 필드는 StableNet 확장으로, 거버넌스 레이어에서 정의한 계정 정책 상태를 표현하는 데 사용됩니다.
상태 객체 라이프사이클
상태 객체는 다음 단계를 거쳐 관리됩니다:- 생성/로드:
getStateObject()가 캐시에서 조회하거나, 없을 경우 trie 또는 스냅샷에서 로드 - 수정: 트랜잭션 실행 중 잔액, nonce, 저장소 값 변경이 메모리에 기록
- 최종화:
Finalise()호출 시 dirty 상태가 pending 상태로 전환 - 커밋:
Commit()호출 시 변경 사항이 trie에 반영되고 데이터베이스에 저장
Trie 구조
상태는 계층적인 Merkle Patricia Trie 구조로 관리됩니다:- 계정 trie (Account Trie): 모든 계정의 상태를 저장하는 단일 trie, 키는 주소 해시
- 저장소 trie (Storage Trie): 각 컨트랙트 계정마다 존재하며, 컨트랙트 변수 값을 저장
| 작업 | StateDB 메서드 | 설명 |
|---|---|---|
| 계정 조회 | getStateObject() | 계정 상태 로드 |
| 계정 기록 | updateStateObject() | 계정 상태 갱신 |
| 계정 삭제 | deleteStateObject() | 계정 제거 |
| 저장소 조회 | GetState() | 저장소 슬롯 조회 |
| 저장소 기록 | SetState() | 저장소 슬롯 갱신 |
캐싱 레이어
상태 접근 비용을 줄이기 위해 다단계 캐싱 구조를 사용합니다:-
StateDB 캐시
stateObject인스턴스를 메모리에 유지합니다. 현재 트랜잭션에서 변경된dirtyStorage, 최종화되었으나 커밋되지 않은pendingStorage, 변경 추적을 위한originStorage를 관리합니다. -
스냅샷 레이어
계정과 저장소를 평면 키-값 구조로 제공하여 trie 순회를 우회합니다. 일반 상태 조회 성능을 크게 개선하며, 비동기적으로 생성·갱신됩니다. -
Trie 노드 캐시
trie 데이터베이스가 관리하며, 최근 접근 노드(clean)와 아직 디스크에 기록되지 않은 수정 노드(dirty)를 분리해 보관합니다. -
디스크 데이터베이스
최종 영구 저장소로, 인코딩된 trie 노드와 상태 데이터를 저장합니다.
저널링 및 스냅샷
저널 시스템은 모든 상태 변경을 순서대로 기록하여, 트랜잭션 단위의 롤백을 가능하게 합니다. 실행 중 오류가 발생하면 해당 트랜잭션에서 발생한 모든 상태 변경을 정확히 되돌릴 수 있습니다.스냅샷 및 되돌리기
Snapshot(): 현재 상태의 되돌리기 기준점을 생성하며, 저널 인덱스를 반환합니다RevertToSnapshot(): 지정된 스냅샷 이후의 모든 변경을 역순으로 복구합니다
- 잔액, nonce, 코드 변경
- 저장소 슬롯 변경
- selfdestruct 플래그
- 접근 목록(access list) 변경
- 로그 추가
상태 커밋
상태 커밋은 블록 단위로 수행되며, 변경 사항을 영구 저장소에 반영하는 과정입니다.커밋 흐름
- dirty 저장소를 pending 저장소로 전환
- 빈 계정 정리(EIP-161)
- 트랜잭션 실행 후 반복 호출
- 각 저장소 trie의 루트 계산
- 계정 trie 갱신
- 현재 상태 루트 반환, 영수증 루트 계산에 사용
- 계정 trie와 저장소 trie를 디스크에 커밋
- trie 데이터베이스 갱신
- 스냅샷 커밋
- 최종 상태 루트 반환
상태 변경 추적
경로 기반 상태 스키마(PBSS)에서는 효율적인 상태 관리와 프루닝을 위해 다음 추적 맵을 유지합니다:- accounts / storages: 현재 블록에서 변경된 계정 및 저장소
- accountsOrigin / storagesOrigin: 변경 전 원본 값
- stateObjectsDestruct: 삭제된 계정 정보
데이터베이스 통합
StateDB는core/state/database.go의 Database 인터페이스를 통해 trie 데이터베이스와 통합됩니다. 해시 기반 스키마와 경로 기반(PBSS) 스키마를 모두 지원합니다.
데이터베이스 구성
| 설정 | 기본값 | 설명 |
|---|---|---|
StateScheme | HashScheme | 상태 저장 스키마 |
TrieCleanLimit | 256 MB | Clean 캐시 크기 |
TrieDirtyLimit | 256 MB | Dirty 캐시 크기 |
SnapshotLimit | 256 MB | 스냅샷 캐시 크기 |
Preimages | false | Trie 키 프리이미지 저장 여부 |
StateHistory | 0 | 상태 히스토리 보관 블록 수 |
OpenTrie(root)OpenStorageTrie(stateRoot, addrHash, root, trie)ContractCode(addrHash, codeHash)TrieDB()
상태 프리페칭
상태 프리페칭은 블록 실행 중 필요한 trie 노드를 사전에 로드하는 최적화 기법입니다.StartPrefetcher()로 시작되고 StopPrefetcher()로 종료됩니다.
프리페처는 트랜잭션이 접근할 계정과 저장소 슬롯을 예측하여 캐시에 적재함으로써, 디스크 I/O 대기를 줄이고 블록 처리 성능을 향상시킵니다.
성능 메트릭
StateDB는 상태 관리 성능을 모니터링하기 위해 다음 메트릭을 수집합니다:- 계정 및 저장소 읽기/쓰기/커밋 시간
- 스냅샷 조회 및 커밋 시간
- Trie 데이터베이스 커밋 시간
- 변경된 계정 및 저장소 슬롯 수

