EIP-7807 SSZ 실행 블록 (SSZ execution blocks)
Hegota 포함 제외 (DFI) · EVM·실행 · 원문 상태 Draft · 2024-10-28 작성
실행 블록의 직렬화 방식을 RLP에서 합의 계층이 쓰는 SSZ로 바꾸고 블록 해시를 SSZ의 hash_tree_root로 계산하게 합니다. 개별 트랜잭션·영수증·출금 항목과 상태 트라이는 그대로 둡니다.
왜 필요한가
지금 이더리움은 같은 체인의 블록을 두 가지 방식으로 표현합니다. 실행 계층 블록은 RLP 인코딩과 머클 패트리샤 트라이를 쓰고 합의 계층은 SSZ(Simple Serialize)를 씁니다. 표현이 달라서 생기는 비용이 큽니다. 합의 계층은 빌더가 약속한 실행 블록 해시를 스스로 확인할 수 없어 실행 계층과 비동기로 통신해야 하고 두 계층을 잇는 엔진 API는 텍스트 기반 JSON이라 데이터가 크고 인코딩·파싱이 느립니다. 블록의 특정 필드 하나를 증명하려 해도 블록 전체가 있어야 합니다. 작업증명(PoW) 시절의 오래된 필드와 비효율적인 로그 블룸(logs bloom)도 그대로 남아 있습니다. 이 EIP는 실행 블록과 블록 해시, 블록 단위 트라이를 SSZ로 옮겨 두 계층의 블록 표현을 통일하고 이런 문제를 정리하려는 제안입니다.
무엇이 바뀌나
지금
- 실행 블록은 RLP와 머클 패트리샤 트라이로, 합의 계층 블록은 SSZ로 따로 표현합니다
- 합의 계층은 실행 블록 해시를 직접 계산하지 못해 실행 계층에 비동기로 확인을 요청합니다
- 엔진 API가 텍스트 JSON으로 데이터를 주고받습니다
- 블록의 한 필드를 증명하려면 블록 전체가 필요합니다
EIP-7807 이후
- 실행 블록도 SSZ로 표현해 두 계층의 블록 표현이 통일됩니다
- 합의 계층이 hash_tree_root로 블록 해시를 스스로 계산해 일찍 일관성을 검증합니다
- 엔진 API를 바이너리 SSZ로 바꿀 수 있어 교환 데이터가 약 50% 줄어듭니다
- 개별 필드만으로 증명할 수 있고 필드 의미가 유지되는 한 증명이 전방 호환됩니다
누구에게 영향이 있나
- 클라이언트 팀: 헤더와 블록을 읽고 쓰는 실행·합의 클라이언트 코드 전반이 바뀝니다. 엔진 API도 바이너리 SSZ 인코딩으로 전환할 수 있습니다.
- 스마트 컨트랙트 개발자: 기존 블록 헤더의 바이너리 포맷이나 keccak256 해시 계산에 의존하는 컨트랙트는 호환성이 깨집니다.
- 지갑·앱 개발자: JSON-RPC 블록 응답의 logsBloom이 전부 1인 값으로 채워지므로, 블룸 필터 대신 영수증을 직접 확인해야 합니다.
- 노드 운영자: 엔진 API의 교환 데이터 크기가 약 50% 줄고 인코딩·파싱 효율이 좋아집니다.
심층 설명
ExecutionPayload 컨테이너
새 실행 블록은 18개 필드를 가진 ProgressiveContainer인 ExecutionPayload로 정의합니다. parent_hash, miner, state_root, transactions, receipts_root, number, gas_limits, gas_used, timestamp, extra_data, mix_hash, base_fees_per_gas, withdrawals, excess_gas, parent_beacon_block_root, requests_hash, block_access_list, slot_number로 구성됩니다. 일반 가스와 블롭 가스는 GasAmounts(GasAmount는 uint64)와 BlobFeesPerGas(FeePerGas는 uint256) 구조로 묶습니다. 트랜잭션과 영수증 목록의 각 원소는 기존 EIP-2718 타입 봉투 표현을 유지하고 출금 원소와 block_access_list는 기존 RLP 표현을 유지합니다. receipts_root는 블록 영수증 목록(ProgressiveList[ProgressiveByteList])의 hash_tree_root이고, requests_hash는 합의 계층이 쓰는 ExecutionRequests의 hash_tree_root와 같습니다.
헤더는 SSZ 요약으로 파생
별도의 헤더 포맷을 두지 않고 실행 블록 헤더를 ExecutionPayload의 SSZ 요약(summary)으로 파생합니다. transactions, withdrawals, block_access_list 세 필드를 각각의 hash_tree_root로 바꾼 형태입니다. 이렇게 정의한 ExecutionPayload는 합의 계층 명세(gloas)의 ExecutionPayload 정의를 대체하며 ExecutionPayloadEnvelope에 실려 전달됩니다. 두 계층이 같은 자료 구조를 쓰게 되므로 합의·실행 통합 구현에서 자료 구조를 공유하기도 쉬워집니다.
블록 해시는 hash_tree_root로 통일
실행 블록 해시를 모든 맥락에서 ExecutionPayload.hash_tree_root()로 계산합니다. BLOCKHASH 명령어, JSON-RPC의 blockHash 필드, devp2p 네트워킹, 그리고 합의 계층의 BeaconState.latest_block_hash와 ExecutionPayloadBid.block_hash 참조가 모두 해당합니다. SSZ 블록 해시는 SHA256 기반으로, 기존 keccak256 기반 블록 해시와 같은 이름 공간을 공유하지만 두 해시 알고리즘이 근본적으로 달라 의미 있는 충돌 위험은 없다고 원문은 설명합니다.
logsBloom은 전부 1로 채움
JSON-RPC 블록 응답의 logsBloom 필드는 모든 비트가 1인 값(256바이트의 0xff)으로 설정해야 합니다(MUST). 비효율적인 로그 블룸 메커니즘을 SSZ 전환을 계기로 버리는 것인데, 전부 1인 값은 '원하는 로그가 있을 수도 있다'는 뜻이라 소비자가 모든 영수증을 직접 확인하도록 유도합니다. 반대로 전부 0인 값은 로그가 확실히 없다는 신호가 되어 로그를 놓치게 만들 수 있어 쓰지 않습니다.
엔진 API와 증명에서 얻는 이득
모든 교환 데이터가 SSZ를 지원하면 엔진 API를 텍스트 JSON에서 바이너리 SSZ 인코딩으로 바꿀 수 있습니다. 교환 데이터 크기가 약 50% 줄고 인코딩·파싱 효율이 크게 좋아집니다. 블록 해시를 공유하므로 중복된 block_hash 필드를 없애고 ForkDigest 컨텍스트 인코딩을 채택할 수 있습니다. 증명 측면에서는 블록 전체 없이 개별 필드 증명이 가능해집니다. EIP-7495 ProgressiveContainer와 EIP-7916 ProgressiveList를 쓰기 때문에 블록 정의가 진화해도 generalized index가 안정적으로 유지되고 목록은 하드코딩된 한도 없이 커질 수 있습니다. 필드의 의미가 변하지 않는 한 증명이 전방 호환되므로 스마트 컨트랙트와 검증 애플리케이션의 유지보수 부담이 줄어듭니다.
기존 컨트랙트와의 호환성
이 EIP는 기존 블록 헤더의 바이너리 포맷에 의존하는 스마트 컨트랙트의 호환성을 깨뜨립니다. 헤더들에 공통 프리픽스가 있다고 가정하고 데이터 전체를 선형으로 keccak256 해시하는 '범용' 구현도 포함됩니다. 한편 JSON-RPC로 logsBloom을 읽는 소비자는 SSZ 블록에서 전부 1인 값을 보게 되는데 이는 하위 호환되는 동작이며 영수증의 logsBloom은 이 EIP의 영향을 받지 않습니다.
진행 상황: Draft, Hegota에서 제외
이 제안은 Draft 상태라 내용이 아직 바뀔 수 있습니다. 코어 개발자 콜에서는 07.30 ACDE #242에서 블록 해시만 SSZ 방식으로 계산하도록 축소한 EIP-7807이 Hegota 포함 후보(PFI)로 접수됐습니다. 합의 계층이 블록 해시를 자체 계산할 수 있어 동기화가 개선된다는 이유였습니다. 그러나 09.10 ACDE #245에서 제외(DFI)가 결정됐습니다. 헤더와 블록을 읽는 모든 코드가 바뀌어야 해 파급 범위(blast radius)가 큰 반면 사용자 이득은 작다는 판단이었습니다.