EIP-3298 스토리지 삭제 환불과 환불 상한 제거 (Remove storage-clear refund and refund cap)

Hegota 포함 검토 (CFI) · 수수료·가스 · 원문 상태 Draft · 2021-02-26 작성

스토리지를 지울 때 주던 SSTORE 환불(STORAGE_CLEAR_REFUND)과 트랜잭션 환불 상한을 없애고 같은 트랜잭션 안에서 되돌린 쓰기에 대한 환불만 남깁니다.

왜 필요한가

가스 환불은 체인이 시작된 프런티어(Frontier) 시절부터 있던 장치입니다. 더 쓰지 않는 스토리지를 지우면 가스를 돌려줘서 상태를 깨끗하게 유지하도록 유도하려는 목적이었습니다. 하지만 원문은 이 유인이 실제로는 작동하지 않았고 상태 증가 문제는 이제 정리 보상이 아니라 상태를 만들 때 직접 값을 매기는 방식(EIP-8037)으로 다룬다고 설명합니다. 반면 환불이 프로토콜에 지우는 부담은 그대로 남아 있습니다. 환불 카운터는 사용 가스의 20%로 제한해야 하고(EIP-3529), 이 상한은 콜데이터 최소 가스(EIP-7623/7976), EIP-8037의 2차원 가스 집계와 복잡하게 얽힙니다. SELFDESTRUCT 환불, EIP-7702 권한별 환불, 블록 단위 집계의 환불은 이미 앞선 EIP들로 제거됐습니다. SSTORE 환불 규칙만 남았는데, 같은 트랜잭션 안에서 되돌린 쓰기를 환불하는 부분은 정당한 순(net) 과금이고 삭제 환불은 더 이상 근거가 없는 트랜잭션 간 유인입니다. 이 EIP는 후자만 걷어냅니다.

무엇이 바뀌나

지금

EIP-3298 이후

누구에게 영향이 있나

심층 설명

남는 환불은 하나: 같은 트랜잭션 안의 쓰기 되돌림

EIP-8038의 STORAGE_WRITE 환불 규칙은 그대로 남습니다. 새 값이 원래 값(original value)과 같고 현재 값(current value)과 다를 때, 즉 같은 트랜잭션 안에서 앞서 바꾼 값을 도로 되돌릴 때 STORAGE_WRITE만큼 환불됩니다. EIP-2200과 같이 원래 값은 트랜잭션 시작 시점의 슬롯 값이고 현재 값은 해당 SSTORE 실행 직전의 값입니다. EIP-8038의 SSTORE 케이스 표에서는 STORAGE_CLEAR_REFUND가 들어간 항목만 지워지고 다른 항목은 바뀌지 않습니다. 환불 카운터의 동작도 그대로입니다. 실행 중에 쌓입니다. 환불을 준 콜 프레임과 함께 저널링되어 되돌려진(revert) 프레임의 몫은 버려지고 트랜잭션 끝 정산에서 한 번 적용됩니다.

STORAGE_CLEAR_REFUND(11,616) 제거

STORAGE_CLEAR_REFUND는 EIP-3529의 SSTORE_CLEARS_SCHEDULE을 잇는 파라미터로, 값은 11,616입니다. 이 EIP는 이 파라미터를 가스 스케줄에서 없애고 이를 쓰는 EIP-8038의 두 환불 규칙도 함께 없앱니다. 원래 값이 0이 아닌 슬롯을 지울 때 환불을 주는 규칙과, 그렇게 지운 슬롯이 복구될 때 그 환불을 되돌리는 규칙입니다. 한편 EIP-8037의 SSTORE 과금 규칙과 상태 가스(state gas) 부과·리필은 건드리지 않습니다. 상태 가스 리필은 환불이 아니므로 환불 카운터에 들어가지 않습니다.

환불 상한 제거: 카운터 전액 차감으로

EIP-8037의 트랜잭션 정산에서 EIP-3529의 상한이 빠집니다. tx_gas_refund = min(tx_gas_used_before_refund // 5, tx_output.refund_counter)였던 것이 단순히 tx_output.refund_counter가 됩니다. 환불 후에도 EIP-7623 콜데이터 플로어는 적용되어 tx_gas_used = max(tx_gas_used_after_refund, calldata_floor_gas_cost)이고, 영수증의 cumulative_gas_used는 플로어 적용 후 값을 씁니다. 블록 단위 집계는 바뀌지 않습니다. EIP-7778과 EIP-8037대로 실행 가스 차원은 환불 전 값인 max(tx_gas_used_before_refund - tx_state_gas, calldata_floor_gas_cost)를 셉니다. EIP-8141이 켜져 있으면 프레임 트랜잭션 정산도 마찬가지로 applied_refund = refund_counter가 되어 MAX_REFUND_QUOTIENT를 쓰지 않습니다.

상한 없이도 안전한 이유: 환불이 스스로 묶여 있음

남는 환불은 같은 트랜잭션에서 이미 낸 요금만 되돌릴 수 있습니다. 슬롯은 쓰기 요금을 내고 값이 바뀐 뒤에야 원래 값으로 복구되어 환불을 받을 수 있고 복구 한 번은 정확히 한 번의 요금을 되돌립니다. 따라서 환불 카운터는 같은 트랜잭션의 STORAGE_WRITE 요금 합을 넘지 못하고 상한이 필요 없습니다. 상한의 또 다른 역사적 역할이던 블록 크기 억제는, 환불을 블록 집계에서 이미 뺀 EIP-7778이 맡고 있습니다. 보안 절은 이 안전성이 두 성질에 달려 있다고 못 박습니다. 환불이 같은 트랜잭션의 요금에 묶여 있다는 점과, 카운터가 revert에 맞춰 저널링된다는 점입니다. 같은 트랜잭션 요금으로 뒷받침되지 않는 환불을 추가하는 미래의 EIP는 상한을 다시 도입하거나 동등한 한계를 증명해야(MUST) 합니다.

삭제 환불을 없애는 근거

삭제 환불은 마지막으로 남은 트랜잭션 간(cross-transaction) 환불입니다. 정리를 유도한다는 목적은 상태 생성 시점에 과금하는 EIP-8037 이후 의미가 없어졌고 EIP-8037 가격 체계에서는 STATE_BYTES_PER_STORAGE_SET × CPSB에 비해 너무 작아 유인으로도, 가스 저금통으로도 작동하지 못합니다. 이제 명세와 구현 부담만 남습니다. 파라미터 하나, 환불 규칙 둘, 그리고 주로 이를 가두려고 존재하는 상한입니다. 원문 주석에 따르면 EIP-8038이 글램스테르담에서 STORAGE_CLEAR_REFUND를 11,616으로 올리고 이 EIP는 그다음 포크를 목표로 이를 완전히 제거합니다.

호환성 영향과 테스트 케이스

하위 호환이 깨지는 가스 리프라이싱이라 예정된 네트워크 업그레이드가 필요합니다. 환불은 트랜잭션 끝 정산에서만 적용되므로 실행 자체는 달라지지 않고 순 비용만 바뀝니다. 원래 값이 0이 아닌 슬롯을 지우는 트랜잭션은 슬롯당 11,616 가스를 더는 돌려받지 못하므로, 환불을 노리고 스토리지 정리를 몰아서 하던 애플리케이션은 그만큼 더 냅니다. 가스 추정기와 지갑은 eth_estimateGas와 수수료 계산에서 삭제 환불과 상한 로직을 빼야(MUST) 합니다. 테스트 표를 보면 x → y → x는 순 비용 0, x → 0은 환불 없이 STORAGE_WRITE를 냅니다. 추가로 많은 슬롯을 지우는 트랜잭션(환불 없음), 환불이 환불 전 사용 가스의 20%를 넘는 경우(상한 없는 차감 확인), 콜데이터 플로어에 걸리는 경우, EIP-7778 블록 집계까지 검증하도록 요구합니다.

현재 상태와 포크 논의

이 EIP는 Draft 상태라 내용이 아직 바뀔 수 있습니다. 콜 기록으로는 08.13 ACDE #243에서 헤고타(Hegota) 후보로 접수(PFI)됐고 09.24 ACDE #246에서 EIP-7906, EIP-8250, EIP-8272, EIP-7668, EIP-8253, EIP-8131 등과 함께 헤고타 포함 검토(CFI) 단계로 올라갔습니다.

EIP 원문 · ← EIPs