[이더리움 Stateless]1. State Expiry & History Expiry

Seongwan Park
Decipher Media |디사이퍼 미디어
14 min readFeb 14, 2023

서울대학교 블록체인 학회 디사이퍼(Decipher) After the merge 팀에서 이더리움의 로드맵 중 하나인 Stateless에 대한 글을 시리즈로 연재합니다. 본 글은 이더리움 Stateless 시리즈의 첫 번째 편으로 Stateless와 버클트리에 대해 설명합니다.

Authors

박성완(Seongwan Park)

Seoul Nat’l Univ. Blockchain Academy Decipher(@decipher-media)

[이더리움 Stateless]

  1. State Expiry & Hisstory Expiry
  2. ASE(Address Space Extension)
  3. 버클 트리 검증
  4. 버클 트리(Verkle Trie) 구조 알아보기

[목차]

  1. Introduction
  2. State Expiry 란?
  3. History Expiry 란?
  4. 맺으며

1. Introduction

블록체인 연구에서 트릴레마 (Blockchain Trilemma) 는 메인 체인이 추구하는 3가지 목표인 탈중앙성 (Decentralization), 안전성 (Security), 확장성 (Scalability) 가 서로 트레이드 오프 관계에 있으며 모두를 달성하는 것이 어렵다는 개념으로 많은 사람들에게 잘 알려져 있습니다. 이더리움 진영에서는 탈중앙화를 가장 우선적인 가치로 여기며, 탈중앙성과 안전성을 해치지 않는 제약조건 하에서 확장성을 개선시킬 수 있는 여러 가지 방안에 대해 고민하고 이를 위한 계획을 로드맵으로 공개하기도 하였습니다.

디사이퍼 After the merge 팀에서 집중하고 있는 두 가지 주제인 MEV와 Stateless 는 서로 연관이 없어 보이는 주제일 수 있지만 두 주제 모두 이더리움의 탈중앙성 강화를 위한 공통적인 목표를 가지고 있습니다. 예컨대, MEV 가 일부 중앙화된 노드들만 얻을 수 있는 혜택이 되어가지 않게 블록 생성을 누구에게나 열어놓자는 아이디어는 강력한 노드들의 중앙화를 막기 위한 움직임입니다. 또한, 이더리움의 체인 규모가 점점 커짐에 따라 체인을 운영할 수 있는 최소 하드웨어 조건이 올라가고 있는데, 소수의 중앙화된 노드들만 이더리움 노드를 운영할 수 있게 되지 않게 체인을 최대한 가볍게 바꾸려는 방향 또한 탈중앙화를 위한 노력이라고 할 수 있습니다. 최근 이더리움 리서치 포럼에서는 Stateless 쪽보다는 MEV 를 중심으로 논의가 진행되고 있지만, 커지는 체인 사이즈 또한 언젠가는 반드시 해결해야 할 문제라는 것은 명확합니다.

이더리움 풀노드와 아카이브 노드 운영을 위한 하드웨어 조건 (출처 https://blog.woodstockfund.com/2022/04/21/deep-dive-into-eip-4444/)

본 글에서는 노드 운영에 필요한 하드웨어 조건을 낮추기 위한 방법으로 거론되고 있는 State Expiry와 History Expiry 에 대해 다루어볼 예정입니다. 간단히 요약하면, State Expiry 는 풀 노드가 유지하는 State Trie 의 일부를 일정 주기마다 지워 Trie의 사이즈가 무한히 늘어나는 것을 막는 것이고, History Expiry 는 일반 이더리움 풀 노드가 아주 오래된 과거 블록 데이터를 아예 지워버리는 것입니다.

2. State Expiry란?

먼저 큰 그림을 이해하기 위해 Stateless에 대해 다시 짚고 넘어가보겠습니다. 이더리움 체인에 새로운 블록이 만들어지고 이 블록에 새로운 트랜잭션들이 담기면, 풀 노드들은 트랜잭션을 실행시켜보고 트랜잭션의 유효성을 검증합니다. 트랜잭션이 유효한 트랜잭션인지를 판단하기 위해서는, 현재 시점에서의 State Trie가 필요합니다. 이 State Trie 에는 현재 시점의 모든 account에 해당하는 상태가 저장되는데, 현재 상태가 특정 트랜잭션을 발생시키는 데 문제가 없는 상태인지를 확인할 수 있습니다. 예컨대 특정 주소 A가 1 ETH를 B에게 송금하는 트랜잭션을 전파시켰는데, A의 현재 상태 상으로 balance가 1 ETH 보다 작으면 이 트랜잭션이 유효하지 않은 트랜잭션임을 판단할 수 있을 것입니다.

이 State Trie에는 이더리움의 제네시스 블록부터 현재까지 1600만 개가 넘는 블록에 등장한 모든 account 들의 state 가 담겨 있습니다. State trie의 사이즈는 50GB 가까이 달하고 트리의 사이즈가 무한히 커지고 있으며 저장 뿐 아니라 검색, 수정에 필요한 리소스도 계속 증가하고 있습니다. 이 account 들 중 90% 이상이 오랜 기간동안 사용되지 않았는데도 불구하고 이들을 모두 저장하고 있기 때문에 비효율적이라고도 이해할 수 있습니다. 이와 관련해서 김재윤 님의 Ethanos : efficient bootstrapping for full nodes on account-based blockchain 논문에서는 오래 사용되지 않은 account들을 제외시켜 State trie의 크기를 줄이는 방법을 제안하기도 하였습니다.

Stateless 라는 개념 또한 이더리움 풀 노드가 가지고 있는 State trie의 사이즈를 줄이자는 것이고, 1) Weak Statelessness 와 2) State Expiry 두 가지 방향으로 나누어 생각할 수 있습니다.

1)Weak Statelessness

먼저 Weak Statelessness 는 블록 제안자만 상태를 저장하고 나머지 검증하는 노드들은 State Trie를 가지고 있지 말자는 아이디어입니다. 현재는 블록 제안자 (Block Proposer) 가 트랜잭션들을 포함한 블록만을 전파시키는데, 만약 블록 제안자가 트랜잭션들과 이 트랜잭션이 유효하다는 증명 (witness) 까지 같이 전파시킬 수 있다면 다른 검증하는 노드들은 굳이 모든 State trie를 가지고 있지 않아도 될 것입니다. Weak Statelessness 는 다수의 검증 노드들이 가벼워지게끔 만들 수는 있지만, 블록 제안자 노드들에게 witness 생성이라는 더 무거운 요구를 하는 것이기 때문에 중앙화를 가속시키는 면도 있습니다. 따라서 Weak Statelessness 만으로는 부족하고, State Expiry 가 필요합니다.

2) State Expiry

State Expiry란 이더리움 풀노드가 유지하는 State Trie를 일정 주기마다 지우고 새로 구성하자는 아이디어입니다. 현재 논의되고 있는 주기는 1년인데, 1년마다 State Trie를 초기화하고 새로 만들게 되면 자연스럽게 노드 입장에서 자주 사용하지 않는 계정 (dormant account) 들의 상태를 저장하고 있을 필요가 없어져서 Trie의 사이즈가 작아지고, Trie가 무한히 커지는 상황을 막을 수 있을 것입니다. 그러나 Trie 를 무작정 지우게 되면 당연하게도 새로운 트랜잭션이 들어왔을 때 트랜잭션을 발생시킨 account가 State Trie에 없다면 트랜잭션의 유효성을 검증하지 못하는 상황이 발생할 것입니다. 이와 같은 상황을 방지하고자 예전 State Trie 에 존재하던 account 의 state 를 복구할 방법이 필수적으로 있어야 합니다.

출처 : https://notes.ethereum.org/@vbuterin/verkle_and_state_expiry_proposal

이더리서치에서 논의되었던 State Expiry 구조 상에서는 1년마다 State Trie를 초기화하며, 이전 period 의 State Trie까지만 저장하고 두 period 이상이 지난 State Trie 는 Trie의 root 값만 저장합니다. 현재를 T period라고 하면 (T-1) State Trie와 (T) State Trie 두 개가 있는 것이고 변경이 일어날 수 있는 것은 (T) State Trie 뿐입니다.

또한 트랜잭션이 어떤 period (이하 epoch)에 있는 account를 접근하는지를 저장하기 위해 현재의 주소 체계 대신 Extended Address Scheme 을 사용합니다. (본 시리즈 글의 2편 참고) 여기서 1년 이라는 기간을 epoch 이라는 용어로 표기하는데, 이더리움 합의알고리즘인 Gasper 에 등장하는 (1epoch = 32slots) 와 다른 epoch임을 기억하겠습니다. Extended Address Scheme 상에서는 현재 사용하는 이더리움 주소 앞에 해당 epoch 넘버를 붙인 (e,s)를 사용하게 됩니다.
(e,s) : e는 epoch, s는 현재 이더리움에서 사용하는 160 bit 주소

(e,s)는 epoch e에 해당하는 주소이므로, 현재 시점의 epoch가 만약 e보다 작다면 (e,s)에 접근할 수 없습니다. 조금더 이해를 하기 쉽게 epoch e 에서의 State Trie 를 S_e 로 표기하겠습니다. State Trie가 참조되는 경우는 다음과 같이 네 가지 경우로 나눌 수 있습니다. 각 상황에서 State 를 올바르게 불러오기 위해 어떠한 정보가 필요한지를 살펴보겠습니다.

(1) Epoch e 에서 (e,s) 의 상태가 변화한 경우

풀노드는 epoch e 의 State Trie 인 S_e 에 접근, 수정할 수 있으므로 현재 이더리움의 풀노드가 State Trie 를 접근할 때와 같은 상황입니다. 풀노드는 epoch e 시점의 State Trie를 가지고 있으므로, (e,s)의 현재 상태를 읽어오고 트랜잭션을 실행시켜 상태를 수정할 수 있습니다.

(2) Epoch e+1 에서 (e,s) 의 상태가 변화한 경우

S_{e+1} 에서 (e,s)가 없고, 현재 풀노드는 이전 시점과 현재 시점의 State Trie인 S_e와 S_{e+1}를 가지고 있습니다. 이전 시점의 State Trie인 S_e를 읽어와서, (e+1,s) 가 S_{e+1} 에 새롭게 추가되거나 변경됩니다.

(3) Epoch f > e+1 에서 (e,s) 의 상태가 변화한 경우

풀노드는 S_f 와 S_{f-1} 만을 저장하고 있습니다. 만약 (e,s) 가 S_f와 S_{f-1} 의 일부에 포함되어 있으면 f-1 또는 f 시점에 (e,s)가 호출되었었다는 뜻이므로 (1) 또는 (2) 와 같은 방식으로 수정될 수 있습니다.

그러나 만약 (e,s) 가 S_f 와 S_{f-1} 에 포함되어있지 않으면 (e,s) 를 변화시키고자 하는 주체는 S_e 에 (e,s)가 존재했다는 witness 와, S_{e+1}, …, S_{f-2} 에 (e,s)가 포함되어있지 않았다는 absence proof 를 같이 생성해서 전파시켜야 합니다. proof 를 생성하고 노드들이 이를 증명하는 방식은 1, 2편에서 다루었던 Verkle tree 와 관련이 있습니다.

정리하면, State Expiry 를 통해 풀노드들은 가장 최근 1–2년의 State Trie 만을 저장하고, 이전 Period 의 State Trie 는 root 만을 저장합니다. 그리고 이전 Period 의 account state 를 불러오기 위해서는 account 의 주인이 가장 최근시점에서의 자신의 state 를 증명하는 witness 와, 특정 시점까지 한번도 사용되지 않았다는 absence proof 를 제출합니다. 이와 같은 스킴을 통해 State Trie의 크기가 무한히 커지는 것을 방지할 수 있습니다.

3. History Expiry

노드들은 제네시스 블록부터 현재 블록 (약 1600만) 까지 모든 블록 데이터, 트랜잭션 데이터와 일부 receipt 데이터를 저장하는데, 규모가 500GB 정도에 달합니다. History Expiry란 풀노드들이 일정 시간 이전의 블록과 트랜잭션 데이터를 모두 다 지우고 최근의 블록들만 저장하는 것입니다. EIP-4444 (History Expiry) 로 알려져 있는데, 여기서 논의되어 왔던 저장 기간은 약 1년입니다.

Historical Data의 용도는 1) Syncing , 2) dAPP 유지 등이 있습니다. geth와 같은 이더리움 클라이언트를 처음 실행하게 되면 peer 노드들과 연결해 블록 데이터를 요청해서 받아오고, 제네시스 블록부터 모든 블록과 트랜잭션 데이터를 실행시켜 현재 블록까지 맞출 수 있습니다. 그런데 만약 세상에 존재하는 모든 이더리움 노드들이 1년 이상 지난 모든 블록 데이터를 다 지우게 된다면, 새로운 노드가 제네시스 블록부터 모든 트랜잭션을 직접 실행시켜보는 것은 불가능할 것입니다. 따라서 특정 checkpoint를 주기적으로 바꾸며 checkpoint가 제네시스 블록처럼 쓰여, checkpoint 이후의 블록과 트랜잭션 데이터만을 받아오게끔 할 수 있습니다. 새롭게 sync를 시도하는 노드들은 처음 자신이 받아오는 checkpoint 를 믿어야 할 것입니다. 이전 삭제된 과거 데이터는, IPFS나 torrent와 같은 분산서버에 저장하자는 제안 또한 포함되어 있습니다.

그리고 dAPP 들이 아주 이전 블록에 있는 트랜잭션들의 정보를 필요로 하는 경우, dAPP 이외의 다른 노드에게 데이터를 요청하는 것이 힘들어질 수 있습니다. dAPP은 직접 트랜잭션과 receipt 데이터를 직접 저장하거나 제네시스 블록부터 모든 트랜잭션 데이터를 가지고 있는 소수의 아카이브 노드들에게 의존해야 할 것입니다.

History Expiry 는 이더리움 프로토콜 레벨의 변화가 아닌, 클라이언트 단에서의 변화입니다. 이더리움에 새로운 규칙이 생기는 것이 아니라, Geth 나 Erigon 과 같은 클라이언트에서 기본값으로 1년 이전의 데이터를 지우는 것입니다. 과거의 블록 데이터를 지우는 것이 언뜻 들으면 매우 위험해보일 수 있는데, 안전성 측면에서 크게 결함이 생기는 것은 아닙니다.

사실 현재 Geth 클라이언트에서도 각 사용자가 자신의 컴퓨터에서 과거 블록 데이터를 지울 수 있습니다. 그리고 다른 노드가 sync를 하기 위해 블록 데이터를 요청했다고 해서 자신이 이를 제공해야 할 의무는 없으며, 선의로 데이터를 보내주고 있는 것입니다. 과거 블록 데이터 저장은 풀노드에게 강제하는 책임이 아닙니다. 지금도 과거 데이터를 가지고 있을 필요가 없는데, 클라이언트 기본값을 바꾸는 것의 의미는 이론상, 또는 최악의 경우에서 큰 의미가 없을 수도 있습니다. 그러나 현실적으로, 클라이언트의 기본값은 클라이언트를 따로 수정하지 않는 대다수의 풀노드의 행동을 제어하기 때문에 이더리움 네트워크 전반에 영향을 미칠 수 있습니다.

데이터 저장에 관한 기본값을 변경하는 것이 위험해보이고, 블록체인이 제 기능을 포기하는 것처럼 들릴 수 있습니다. 그러나 이더리움은 상태 머신 (state machine) 이며, 과거 데이터 보존을 위한 목적으로 존재하는 것이 아니라, 새로운 트랜잭션이 들어왔을 때 이를 실행하고 현재 상태에 합의하여 새로운 상태에 도달하는 역할이 주된 목적입니다. checkpoint를 믿을 수 있다면 그 이후의 트랜잭션들을 받아 직접 실행시켜 현재까지 sync하는 데는 문제가 없으며, checkpoint를 믿는 것은 현재 genesis block을 믿는 것과 크게 다르지 않습니다.

4. 맺으며

본 글에서는 Stateless Ethereum 을 위한 로드맵의 일부인 Stateless Expiry와 History Expiry 에 대해 다루어 보았습니다. 요약하면, 클라이언트를 가볍게 만들기 위해 State Trie 의 사이즈가 무한히 커지는 것을 막고, 노드가 가지고 있는 과거 블록 데이터 중 오랜 기간이 지난 데이터는 지운다는 아이디어입니다. 글에서 자세히 다루지 않았던 State Expiry 상의 주소 체계인 ASE (Address Space Extension) 에 대해서는 2편에서 자세히 다루어볼 예정입니다.

References

https://notes.ethereum.org/@vbuterin/state_expiry_eip

https://blog.woodstockfund.com/2022/04/21/deep-dive-into-eip-4444/

https://notes.ethereum.org/@vbuterin/verkle_and_state_expiry_proposal

https://ethereum-magicians.org/t/eip-4444-bound-historical-data-in-execution-clients/7450

--

--