EIP-150 IO 집약 연산의 가스비 인상 (Gas cost changes for IO-heavy operations)
최종 확정 · 수수료·가스 · 원문 상태 Final · 2016-09-24 작성
디스크에서 상태를 읽는 EVM 연산의 가스비를 실제 비용에 맞게 올리고 호출에 넘길 수 있는 가스를 '남은 가스의 63/64'로 제한합니다. 2016년 서비스 거부 공격에 대응해 블록 2,463,000에서 적용된 하드포크입니다.
왜 필요한가
2016년 이더리움은 서비스 거부(DoS) 공격을 연이어 겪었습니다. 공격자는 계정 잔액이나 컨트랙트 코드처럼 디스크에 저장된 상태를 읽는 연산이 실제 처리 비용에 비해 턱없이 싸다는 점을 노렸습니다. 디스크 읽기는 시간이 오래 걸리는데, 가스비가 20~50 수준이라 적은 돈으로 노드들에게 막대한 읽기 작업을 시킬 수 있었던 것입니다. 소프트웨어 최적화만으로는 이 불균형이 사라지지 않고 이런 공격 트랜잭션은 장차 샤딩 설계에서도 수십 메가바이트의 머클 증명(Merkle proof)을 요구해 위험 요소가 됩니다. 그래서 이 EIP는 상태를 읽는 연산들의 가스비를 올려 스팸 공격의 비용을 실제 부담에 맞추고 그 과정에서 기존 컨트랙트가 깨지지 않도록 가스 전달 방식도 함께 손봤습니다.
무엇이 바뀌나
지금
- SLOAD는 50, BALANCE·EXTCODESIZE는 20, CALL은 40 등 상태를 읽는 연산이 매우 쌉니다
- 싼 상태 읽기 연산을 대량으로 반복하는 스팸 트랜잭션으로 네트워크 성능을 떨어뜨릴 수 있습니다
- 호출 스택 깊이가 약 1024까지 허용되어, 이를 악용한 '콜스택 깊이 공격'을 컨트랙트 개발자가 직접 방어해야 합니다
EIP-150 이후
- SLOAD는 200, BALANCE는 400, EXTCODESIZE·EXTCODECOPY·CALL 계열은 700으로 가스비가 오릅니다
- 블록 하나를 처리할 때 읽어야 하는 데이터가 제한되어 같은 공격에 훨씬 큰 비용이 듭니다
- 호출에는 남은 가스의 63/64까지만 넘길 수 있어, 가스가 자연스럽게 호출 깊이를 약 340으로 제한하고 콜스택 깊이 공격이 공격 유형 자체로 사라집니다
- 평균적인 처리량을 유지하도록 권장 가스 한도 목표가 550만으로 올라갑니다
누구에게 영향이 있나
- 컨트랙트 개발자: msg.gas - 40 같은 표현으로 호출 가스를 계산하던 기존 컨트랙트도 63/64 규칙 덕분에 계속 동작합니다. 콜스택 깊이 공격을 따로 방어할 필요가 없어집니다.
- 사용자: 상태 읽기나 외부 호출이 많은 트랜잭션은 수수료가 오릅니다.
- 클라이언트 팀·노드 운영자: 블록 2,463,000에서 하드포크로 적용되며 블록당 읽기 부담이 줄어 노드 운영이 안정됩니다.
- 채굴자: 권장 가스 한도 목표를 550만으로 올리는 것이 권고됩니다.
심층 설명
적용 시점과 포크
메인넷(CHAIN_ID 1) 기준 블록 번호 2,463,000(FORK_BLKNUM)부터 적용됩니다. 이 EIP는 Tangerine Whistle 하드포크(EIP-608이 메타로 정리)의 내용이며, 상태가 Final인 확정 명세입니다.
상태 읽기 연산의 가스비 인상 내역
EXTCODESIZE는 20에서 700으로, EXTCODECOPY의 기본 비용은 20에서 700으로, BALANCE는 20에서 400으로, SLOAD는 50에서 200으로 올립니다. CALL·DELEGATECALL·CALLCODE는 40에서 700으로 올립니다. 모두 상태 트리를 읽어 디스크 접근을 일으키는 연산들입니다.
SELFDESTRUCT 비용 신설
SELFDESTRUCT의 가스비를 0에서 5000으로 올립니다. 또 SELFDESTRUCT가 새로 생성되는 계정을 대상으로 하면, CALL과 비슷하게 25000의 추가 가스비가 붙습니다. 계정 생성이 공짜로 일어나던 경로를 막는 조치입니다.
63/64 규칙: EIP-90과 EIP-114의 결합
'all but one 64th'를 N - floor(N / 64)로 정의합니다. 호출이 허용 한도보다 많은 가스를 요구하면 OOG 에러를 내는 대신, 남은 가스의 63/64만 넘겨 호출을 진행합니다. 구현은 max_call_gas(gas) = gas - (gas // 64)를 두고, 남은 가스가 요청량에 못 미치면 gas = min(gas, max_call_gas(남은 가스 - extra_gas))로 바꾸는 방식입니다. CREATE도 부모 가스의 63/64만 자식에게 제공합니다. 이 장치가 없으면 msg.gas - 40처럼 호출 비용이 40이라는 가정에 기댄 기존 컨트랙트가 전부 멈추기 때문에, 비용 인상과 함께 도입되었습니다(EIP-90 메커니즘). 동시에 EIP-114를 결합해 고정된 콜스택 깊이 제한을 가스 기반의 부드러운 제한으로 바꿨습니다.
비용 산정 근거
새 가스비는 1.0 가스비를 만들 때 쓴 계산표의 갱신판에서 도출했습니다. 블록 하나를 처리할 때 읽어야 하는 데이터를 8MB 이내로 제한하는 것을 목표로 하고 머클 증명 크기를 SLOAD는 500바이트, 계정은 1000바이트로 추정했습니다. 여기에 코드 로딩 비용(최악의 경우 약 17~21kb)을 반영해 계산값 위에 300 가스의 고정 가산을 얹었습니다. 명세를 단순하게 유지하는 것이 설계 목표였습니다.
콜스택 깊이 공격의 제거
호출마다 가스의 1/64이 부모에 남으므로, 주어진 파라미터에서 사실상 최대 호출 깊이는 약 1024에서 약 340으로 줄어듭니다. 덕분에 호출 스택을 일부러 깊게 쌓아 하위 호출을 실패시키는 공격이 컨트랙트 개발자가 신경 써야 할 공격 유형에서 사라지고 호출에 의존하는 2차 복잡도(quadratic-complexity) DoS 공격의 피해도 완화됩니다.
가스 한도 상향 권고
연산 비용이 전반적으로 오르면 같은 가스 한도로 처리할 수 있는 트랜잭션 수가 줄어듭니다. 평균적인 컨트랙트 기준으로 기존의 초당 처리 능력을 유지하기 위해 권장 가스 한도 목표를 550만으로 올립니다.