EIP-7907 컨트랙트 코드 크기 가스 과금 (Meter Contract Code Size)

Hegota 포함 제안 (PFI) · 수수료·가스 · 원문 상태 Draft · 2025-03-14 작성

24KB를 넘는 컨트랙트 코드를 불러올 때 초과분 32바이트마다 2가스를 더 받습니다. EIP-7954로 64KB까지 커지는 컨트랙트가 노드에 시키는 일만큼 가스를 내게 하는 제안입니다.

왜 필요한가

이더리움은 EIP-170에서 컨트랙트 코드 크기를 24KB로 제한했습니다. 큰 코드는 디스크에서 읽는 양, 실행 전 전처리, 블록 증명 크기가 모두 코드 길이에 비례해 늘어나는데, 이 비용을 가스로 따로 받지 않았기 때문입니다. 한도를 걸어 서비스 거부(DoS) 공격을 막은 셈입니다. EIP-7954는 이 한도를 64KB로 올려 더 큰 컨트랙트를 배포할 수 있게 합니다. 그런데 한도만 올리고 비용은 그대로 두면, 큰 컨트랙트를 불러오는 쪽이 노드에 더 많은 일을 시키면서도 같은 가스만 냅니다. 이 EIP는 24KB를 넘는 부분에 크기만큼 가스를 더 받아, 쓴 자원만큼 내는 이더리움 가스 모델의 원칙을 지킵니다.

무엇이 바뀌나

지금

EIP-7907 이후

누구에게 영향이 있나

심층 설명

과금 공식과 상수

추가 비용은 EXCESS_CODE_COST = ceil32(excess_code_size(len(code))) * GAS_PER_CODE_WORD // 32 로 계산합니다. excess_code_size(n) = max(0, n - 0x6000) 이므로 0x6000(24576바이트) 이하 코드는 초과분이 0이고 그보다 크면 넘는 길이만 셉니다. ceil32는 32바이트 단위로 올림하는 함수입니다. GAS_PER_CODE_WORD는 2로, 초과분 한 워드(32바이트)마다 2가스를 더 받습니다. 공식에 넣어 계산하면 EIP-7954의 최대치인 65536바이트 코드는 초과분이 40960바이트, 1280워드이므로 2560가스가 추가됩니다. 함께 쓰이는 COLD_SLOAD_COST 2100, WARM_STORAGE_READ_COST 100, COLD_ACCOUNT_ACCESS_COST 2600은 EIP-2929의 값을 그대로 가져옵니다. MAX_CODE_SIZE와 MAX_INITCODE_SIZE 한도 자체는 EIP-7954에 정한 대로 두고 바꾸지 않습니다. 활성화 블록 FORK_BLKNUM은 아직 정해지지 않았습니다(TBD).

코드의 콜드·웜 상태와 적용 명령어

계정과 저장소에만 있던 콜드·웜 구분을 컨트랙트 코드에도 둡니다. 코드를 불러오는 명령어인 CALL, STATICCALL, DELEGATECALL, CALLCODE, EXTCODECOPY의 가스표가 바뀌어, 코드가 콜드일 때 EXCESS_CODE_COST를 접근 비용에 더합니다. 원문 표는 세 경우로 나눕니다. 계정과 코드가 모두 콜드면 COLD_ACCOUNT_ACCESS_COST 2600에 COLD_SLOAD_COST 2100과 EXCESS_CODE_COST를 더합니다. 계정은 웜(접근 목록에 있거나 잔액·저장소를 먼저 건드린 경우)인데 코드가 콜드면 WARM_STORAGE_READ_COST 100에 2100과 EXCESS_CODE_COST를 더합니다. 계정과 코드가 모두 웜이면 100만 냅니다. 호출 대상이 EIP-7702로 다른 계정에 위임한 주소이면, 위임 대상 계정의 코드가 콜드일 때 추가 가스를 더 셉니다. 코드를 웜으로 바꾼 기록은 저널링 대상이라 EIP-2930의 다른 상태 웜 처리처럼 revert 때 되돌아갑니다. 빈 코드(keccak("") = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)는 항상 웜으로 봅니다.

트랜잭션 진입점이 큰 컨트랙트일 때

트랜잭션의 to 주소 자체가 24KB를 넘는 컨트랙트이면 호출 명령어를 거치지 않고 바로 그 코드를 불러오게 됩니다. 이 경우 1번 규칙으로 계산한 비용을 실행에 들어가기 전에 먼저 받고 그 컨트랙트 코드를 웜으로 표시합니다. 이 비용은 트랜잭션의 기본 가스(initial gas fee)에 합산해 계산하지 않고 따로 뗍니다. 이 단계에서 가스가 모자라 실행이 멈추면(out-of-gas halt) 실행은 거기서 끝나고 잔액 이체도 일어나지 않습니다.

계정 튜플에 codesize 추가와 마이그레이션

계정을 나타내는 튜플이 (nonce, balance, storage_root, code_hash)에서 (nonce, balance, storage_root, code_hash, codesize)로 바뀝니다. 운영 중인 클라이언트가 쓰는 키-값 데이터베이스 대부분은 값 전체를 읽지 않고는 그 값의 크기를 알 수 없기 때문입니다. 전체 상태를 한 번에 옮기는 마이그레이션은 피하고 두 규칙을 둡니다. 첫째, FORK_BLKNUM 이후에 생성되거나 갱신된 계정은 codesize를 튜플에 같이 씁니다. 둘째, codesize 필드가 없는 계정은 과금에 필요할 때 바이트코드 길이를 재서 크기를 구합니다. 이런 계정에는 EIP-7954 활성화 뒤 배포된 24KB 초과 코드가 들어 있을 수 있습니다.

워드당 2가스와 24KB 바닥을 고른 이유

워드당 2가스는 세 가지 비용을 반영합니다. 큰 코드를 가져오는 디스크 입출력, 실행 전에 코드를 전처리하는 연산(JUMPDEST 분석), 큰 컨트랙트가 든 블록의 머클 증명 크기 증가입니다. 공식은 EIP-170의 24KB 한도를 넣지 않고 ceil32(contract_size) * GAS_PER_CODE_WORD // 32 처럼 더 단순하게 쓸 수도 있었습니다. 그러나 보수적으로 가기 위해, 그리고 24KB 아래인 기존 컨트랙트의 로딩 비용을 낮추는 일이 없도록 24KB 바닥을 공식에 넣었습니다. 그래서 이 EIP의 과금은 24KB를 넘는 컨트랙트에만 붙는 추가 비용입니다.

EXTCODECOPY 예외와 하위 호환성

EXTCODECOPY는 요청한 구간만 읽으면 되므로 이론상 과금에서 뺄 여지가 있습니다. 그러나 블록 증인(witness)에는 코드 전체가 필요해, 예외를 두려면 프로토콜 차원의 변경이 더 필요할 수 있습니다. 그래서 일단 EXTCODECOPY도 과금 대상에 넣고 예외는 나중에 검토할 수 있다고 적었습니다. 하위 호환성 절은 CALL, STATICCALL, DELEGATECALL, CALLCODE, EXTCODESIZE, EXTCODECOPY가 콜드 코드에 접근할 때 추가 가스가 들 수 있다고 적습니다. Behavior 절의 명령어 목록에는 없던 EXTCODESIZE가 여기에는 들어 있습니다.

초안 상태와 포크 논의

이 EIP는 Draft 상태라 내용이 바뀔 수 있습니다. Test Cases, Reference Implementation, Security Considerations 절은 아직 비어 있고 활성화 블록도 TBD입니다. 코어 개발자 콜에서는 10.08 ACDE #247에서 EIP-7907을 Hegota PFI(Proposed for Inclusion) 목록에 추가하고 결정은 다음 콜로 미뤘습니다. 글램스테르담이 컨트랙트 크기 한도만 올리고 크기 기반 과금을 빼면서 생긴 DoS 여지를 메우려는 제안으로 다뤄졌습니다.

EIP 원문 · ← EIPs