EIP-8304 무신뢰 로그·트랜잭션 인덱스 (Trustless log and transaction index)
Hegota 포함 제외 (DFI) · 확장성·데이터 · 원문 상태 Draft · 2026-06-17 작성
블록마다 로그·트랜잭션 인덱스 테이블을 만들고 그 루트 해시를 시스템 컨트랙트에 저장합니다. 누구나 로그·트랜잭션 조회 결과가 맞는지 증명으로 검증할 수 있게 됩니다.
왜 필요한가
이더리움에서 로그(log)는 스마트 컨트랙트가 남기는 이벤트 기록으로, 상태를 쓰는 것보다 훨씬 싼 가스 비용으로 남길 수 있습니다. 그런데 프로토콜은 이 로그를 찾아볼 효율적인 방법을 제공하지 않습니다. 블록마다 들어 있는 블룸 필터는 초기에는 어느 정도 쓸 만했지만 지금은 포화 상태라 사실상 쓸모가 없습니다. 그래서 대부분의 사용자는 원격 제공자(RPC 서비스)에 의존하는데, 이들은 조회 결과가 올바르다는 것을 증명하지 못합니다. 컨트랙트를 무신뢰 분산 장부에 올려 두고 그 출력은 믿음으로 받아야 한다면 본래 목적이 무색해집니다. 이더리움의 처리량이 커질수록 이 정보 비대칭은 더 나빠집니다. 이 EIP는 상태 트리보다 훨씬 싸게 갱신할 수 있으면서도 적당한 크기의 증명으로 조회 결과를 검증할 수 있는 인덱스 구조를 제안합니다. 로그의 낮은 가스 비용은 그대로 유지하면서 로그의 쓸모를 키우는 것이 목표입니다.
무엇이 바뀌나
지금
- 블록 헤더의 블룸 필터가 포화 상태라 로그 검색에 사실상 쓸모가 없습니다
- 대부분의 사용자가 원격 제공자의 로그·트랜잭션 조회 결과를 증명 없이 믿어야 합니다
- 다른 체인에서 일어난 이벤트를 받아오려면 신뢰할 중개자가 필요합니다
EIP-8304 이후
- 블록 처리의 일부로 인덱스 테이블 루트가 시스템 컨트랙트에 기록됩니다
- 로그·트랜잭션 조회 결과를 적당한 크기의 증명으로 검증할 수 있습니다
- 이 EIP를 구현한 다른 체인의 최근 블록 해시만 알면 그 체인의 로그를 증명으로 받아올 수 있습니다
- LOG 연산의 가스 비용은 오르지 않고 헤더의 블룸 필터도 그대로 남습니다
누구에게 영향이 있나
- 사용자: 원격 제공자를 신뢰하지 않고도 로그·트랜잭션 조회 결과가 맞는지 검증할 수 있습니다.
- 지갑·앱 개발자: 풀노드 없이도 증명 기반으로 이벤트를 조회하는 앱과 라이트 클라이언트를 만들 수 있습니다.
- 클라이언트 팀: 블록 처리에 인덱스 테이블 생성·비동기 병합과 시스템 컨트랙트 호출 로직을 구현해야 합니다.
- L2·크로스체인: 로그에는 시간 축이 있으므로 지난 갱신 이후의 이벤트를 증명으로 받아 차등 갱신하는 크로스체인 메시징에 쓸 수 있습니다.
심층 설명
다섯 레벨의 테이블과 1024칸 링 버퍼
인덱스 테이블은 다섯 레벨로 유지합니다. 각 레벨의 테이블은 TABLE_SIZES = [1, 4, 16, 64, 256]개 블록을 담고 first_block은 항상 table_size의 배수이며 TABLE_SIZES[i]는 TABLE_SIZES[i-1]의 배수입니다. 그래서 상위 테이블 하나는 바로 아래 레벨 테이블 4개(고정 개수)를 병합해 만들 수 있습니다. 레벨 0의 단일 블록 테이블은 그 블록의 트랜잭션을 모두 처리한 직후 바로 기록하고 상위 레벨 테이블은 TABLE_SIZES[i] // 4 블록 지연 후에 기록합니다. 이 지연 덕분에 병합을 블록 처리와 비동기로 진행할 수 있어 블록 처리 지연이 늘지 않습니다. 각 레벨의 루트 해시는 TABLES_PER_LEVEL = 1024 길이의 링 버퍼에 저장합니다. 공비 4는 절충입니다. 공비가 더 작으면 레벨 수가 늘어 트리 해싱이 많아지고 더 크면 증명에 필요한 작은 테이블 수가 늘어 증명 크기가 커집니다.
7가지 엔트리 타입과 정렬·해싱 규칙
엔트리 타입은 블록(0, 블록 해시), 트랜잭션(1, 트랜잭션 해시), 로그 주소(2), 로그 토픽 0~3(3~6)의 7가지입니다. 각 엔트리는 타입 ID, 검색 대상 값, 위치 정보(블록 번호, 트랜잭션 인덱스, 로그 인덱스)로 구성되고 인코딩 길이는 타입별로 38~50바이트입니다. 트랜잭션 엔트리에는 그 트랜잭션 이전까지 블록에 쌓인 로그 수(cumulative log count)가 들어가고 주소·토픽 엔트리의 로그 인덱스는 트랜잭션 시작 기준 상대값입니다. 모든 수는 빅엔디언으로 인코딩해 이진 표현의 사전순 정렬이 곧 올바른 순서가 되게 합니다. 블록 엔트리는 한 블록 지연으로 추가합니다. 블록 N을 처리할 때 부모 블록 N-1의 해시를 넣는데, 현재 블록의 해시는 그 블록 안에서 아직 정해지지 않았기 때문입니다. 제네시스 블록의 블록 엔트리는 없습니다. 테이블 루트는 엔트리들의 SHA2-256 해시를 담은 SSZ List[Hash32, entry_count]의 루트로 계산합니다.
인덱스 컨트랙트의 set과 get
테이블 루트는 SYSTEM_ADDRESS(0xfffffffffffffffffffffffffffffffffffffffe)가 INDEX_CONTRACT_ADDRESS를 호출해 기록합니다. 입력은 first_block, table_size, table_root 각 32바이트, 가스 한도 30_000_000, 값 0입니다. EIP-4788과 같은 시스템 호출 규약이라 호출은 끝까지 실행돼야 하고 블록 가스 한도에 계산되지 않으며 EIP-1559 소각 의미론을 따르지 않습니다. 해당 주소에 코드가 없으면 조용히 실패합니다. 호출자가 SYSTEM_ADDRESS면 set, 아니면 get이 실행됩니다. 저장 위치는 table_size * TABLES_PER_LEVEL + (first_block // table_size) % TABLES_PER_LEVEL 슬롯입니다. get은 calldata가 64바이트가 아니거나, first_block이 table_size의 배수가 아니거나, 요청한 루트가 아직 기록되지 않았거나 링 버퍼에서 이미 덮어써졌거나, 저장된 값이 0이면 revert합니다. 이 함수는 EVM 안에서 온체인 검증에 쓰거나, 실행 증인과 함께 오프체인 조회 증명의 일부로 쓸 수 있습니다.
동기화 후 초기화와 무상태 실행 경로
이 EIP는 동기화 프로토콜을 확장하지 않습니다. 필요한 테이블은 모두 최근 블록에서 다시 만들 수 있기 때문입니다. 최악의 경우에도 최근 TABLE_SIZES[len(TABLE_SIZES)-1] * 5 // 4 - 1개 블록, 현재 파라미터로 319개 블록만 있으면 최상위 테이블 루트를 만들 수 있습니다. 완전한 무상태 프로토콜에서는 부분 병합 테이블의 증인으로 병합 구현이 가능합니다. 병합 테이블의 예상 entry_count를 기준으로 균등한 entry_index 체크포인트를 정하고 각 체크포인트에서 부분 병합 테이블의 머클 경계 증명을 증인 세트에 포함하게 하면, 블록 생산자가 다음 병합 조각을 커밋하고 검증자는 하위 레벨 테이블 조각의 증인으로 이를 검증합니다. 같은 접근을 실행 ZKP 생성에도 적용할 수 있고 병합을 비동기로 증명하는 선택지도 있습니다.
프로토콜 안은 256블록까지, 긴 역사는 ZKP로
프로토콜은 최대 256블록짜리 테이블까지만 만들고, 수백만 블록 규모의 큰 테이블은 인프라 운영자가 만들어 ZKP로 증명한 뒤 같은 get 인터페이스를 가진 별도의 히스토리 인덱스 컨트랙트에 올리는 구조를 가정합니다. 큰 테이블을 프로토콜 안에서 만들면 모든 노드에 부담이 크고 모든 노드에 수백만 블록의 히스토리가 있다는 가정이 필요해 현재 계획 기준으로 미래 호환적이지 않기 때문입니다. 반대로 인덱싱을 전부 외부 증명자에게 맡기면 마지막 블록은 결코 인덱싱되지 않고 보통 가장 궁금한 최근 구간이 비어 의미 있는 증명에 마지막 갱신 이후의 모든 블록 영수증이 들어가야 합니다. 링 버퍼 덕분에 대략 최근 2**18개 블록이 256블록 테이블로 덮이고 가장 최근의 증명된 히스토리 테이블이 36일보다 오래되지 않으면 전체 체인이 검색 가능합니다. 일반적인 조회 증명은 30~50개의 개별 테이블 증명으로 구성될 것으로 예상합니다.
가스 비용, 보안, 확장 여지
단일 블록 테이블의 생성 비용이 낮고 큰 테이블은 블록 사이에 비동기로 병합되므로 LOG 연산의 가스 비용은 올릴 필요가 없습니다. 보안 측면에서는 자주 갱신되는 저장 경로(브랜치)를 가진 컨트랙트에 소량의 ETH를 뿌려 트리 분기를 오염시키는 공격이 거론되지만 상태 루트 갱신을 의미 있게 늦추려면 공격 비용이 크게 치솟는다고 판단했습니다. 확장 여지도 설계에 들어 있습니다. 엔트리가 타입 ID로 시작하므로 새 종류의 이벤트를 담는 새 엔트리 타입을 문제없이 추가할 수 있고 위치 정보의 블록 번호 필드 뒤는 기존 블록을 다시 인덱싱하지 않고도 인코딩을 바꾸거나 늘릴 수 있습니다. EIP-8141이 활성화되면 트랜잭션 인덱스와 로그 인덱스 사이에 2바이트 프레임 인덱스가 추가됩니다. 나아가 최근 몇 블록의 로그가 EVM에서 조회 가능해지면, 쓰기가 아주 싸고 만료가 쉬우며 병렬 실행에 친화적인 대안적 상태로도 로그를 쓸 수 있다는 구상이 동기 절에 담겨 있습니다.
Draft 상태, 헤고타 포크에서는 제외
이 EIP는 Draft 상태라 명세가 아직 바뀔 수 있습니다. 실제로 INDEX_CONTRACT_ADDRESS와 배포 트랜잭션의 서명 값 등은 원문에 TBD로 남아 있습니다. 코어 개발자 콜에서는 07.30 ACDE #242와 08.27 ACDE #244에서 결정 없이 논의됐습니다. 09.10 ACDE #245에서는 결정을 2주 미루면서 베수(Besu)와 레스(Reth)가 비동기로 논의에 참여하기로 했고 진전이 없으면 DFI가 기본값이라고 정리했습니다. 이어 09.24 ACDE #246에서 헤고타(Hegota)에서 제외했습니다. 이번 포크 후보에서 최종적으로 빠졌다는 뜻입니다.